﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
// deelx.h
//
// DEELX Regular Expression Engine (v1.2)
//
// Copyright 2006 (c) RegExLab.com
// All Rights Reserved.
//
// http://www.regexlab.com/deelx/
//
// Author: 史寿伟 (sswater shi)
// sswater@gmail.com
//
// $Revision: 680 $
//

#ifndef __XOSKIT_XOS_CORE_XOS_STL_SRC_DEELX_DEELX_H__
#define __XOSKIT_XOS_CORE_XOS_STL_SRC_DEELX_DEELX_H__

#include <memory.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 加上名字空间

namespace xos_stl
{

    extern "C" {
        typedef int (*POSIX_FUNC)(int);
        int is_blank(int c);
    }

    //
    // Data Reference
    //
    template <class ELT> class CBufferRefT
    {
    public:
        CBufferRefT(const ELT * pcsz, int length);
        CBufferRefT(const ELT * pcsz);

    public:
        int nCompare      (const ELT * pcsz) const;
        int nCompareNoCase(const ELT * pcsz) const;
        int  Compare      (const ELT * pcsz) const;
        int  CompareNoCase(const ELT * pcsz) const;
        int  Compare      (const CBufferRefT <ELT> &) const;
        int  CompareNoCase(const CBufferRefT <ELT> &) const;

        ELT At          (int nIndex, ELT def = 0) const;
        ELT operator [] (int nIndex) const;

        const ELT * GetBuffer() const;
        int GetSize() const;

    public:
        virtual ~CBufferRefT();

        // Content
    protected:
        const ELT * m_pRef;
        int         m_nSize;
    };

    //
    // Implemenation
    //
    template <class ELT> CBufferRefT <ELT> :: CBufferRefT(const ELT * pcsz, int length)
    {
        m_pRef  = pcsz;
        m_nSize = length;
    }

    template <class ELT> CBufferRefT <ELT> :: CBufferRefT(const ELT * pcsz)
    {
        m_pRef  = pcsz;
        m_nSize = 0;

        if(pcsz != 0) while(m_pRef[m_nSize] != 0) m_nSize ++;
    }

    template <class ELT> int CBufferRefT <ELT> :: nCompare(const ELT * pcsz) const
    {
        for(int i=0; i<m_nSize; i++)
        {
            if(m_pRef[i] != pcsz[i])
                return m_pRef[i] - pcsz[i];
        }

        return 0;
    }

    template <class ELT> int CBufferRefT <ELT> :: nCompareNoCase(const ELT * pcsz) const
    {
        for(int i=0; i<m_nSize; i++)
        {
            if(m_pRef[i] != pcsz[i])
            {
                if(toupper((int)m_pRef[i]) != toupper((int)pcsz[i]))
                    return m_pRef[i] - pcsz[i];
            }
        }

        return 0;
    }

    template <class ELT> inline int CBufferRefT <ELT> :: Compare(const ELT * pcsz) const
    {
        return nCompare(pcsz) ? 1 : (int)pcsz[m_nSize];
    }

    template <class ELT> inline int CBufferRefT <ELT> :: CompareNoCase(const ELT * pcsz) const
    {
        return nCompareNoCase(pcsz) ? 1 : (int)pcsz[m_nSize];
    }

    template <class ELT> inline int CBufferRefT <ELT> :: Compare(const CBufferRefT <ELT> & cref) const
    {
        return m_nSize == cref.m_nSize ? nCompare(cref.GetBuffer()) : 1;
    }

    template <class ELT> inline int CBufferRefT <ELT> :: CompareNoCase(const CBufferRefT <ELT> & cref) const
    {
        return m_nSize == cref.m_nSize ? nCompareNoCase(cref.GetBuffer()) : 1;
    }

    template <class ELT> inline ELT CBufferRefT <ELT> :: At(int nIndex, ELT def) const
    {
        return nIndex >= m_nSize ? def : m_pRef[nIndex];
    }

    template <class ELT> inline ELT CBufferRefT <ELT> :: operator [] (int nIndex) const
    {
        return nIndex >= m_nSize ? 0 : m_pRef[nIndex];
    }

    template <class ELT> const ELT * CBufferRefT <ELT> :: GetBuffer() const
    {
        static const ELT _def[] = {0}; return m_pRef ? m_pRef : _def;
    }

    template <class ELT> inline int CBufferRefT <ELT> :: GetSize() const
    {
        return m_nSize;
    }

    template <class ELT> CBufferRefT <ELT> :: ~CBufferRefT()
    {
    }

    //
    // Data Buffer
    //
    template <class ELT> class CBufferT : public CBufferRefT <ELT>
    {
    public:
        CBufferT(const ELT * pcsz, int length);
        CBufferT(const ELT * pcsz);
        CBufferT();

    public:
        ELT & operator [] (int nIndex);
        const ELT & operator [] (int nIndex) const;
        void  Append(const ELT * pcsz, int length, int eol = 0);
        void  Append(ELT el, int eol = 0);

    public:
        void  Push(ELT   el);
        int   Pop (ELT & el);
        int   Peek(ELT & el) const;

    public:
        const ELT * GetBuffer() const;
        ELT * GetBuffer();
        ELT * Detach();
        void  Release();
        void  Prepare(int index, int fill = 0);
        void  Restore(int size);

    public:
        virtual ~CBufferT();

        // Content
    protected:
        ELT * m_pBuffer;
        int   m_nMaxLength;
    };

    //
    // Implemenation
    //
    template <class ELT> CBufferT <ELT> :: CBufferT(const ELT * pcsz, int length) : CBufferRefT <ELT> (0, length)
    {
        m_nMaxLength = CBufferRefT <ELT> :: m_nSize + 1;

        CBufferRefT <ELT> :: m_pRef = m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength);
        memcpy(m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT <ELT> :: m_nSize);
        m_pBuffer[CBufferRefT <ELT> :: m_nSize] = 0;
    }

    template <class ELT> CBufferT <ELT> :: CBufferT(const ELT * pcsz) : CBufferRefT <ELT> (pcsz)
    {
        m_nMaxLength = CBufferRefT <ELT> :: m_nSize + 1;

        CBufferRefT <ELT> :: m_pRef = m_pBuffer = (ELT *) malloc(sizeof(ELT) * m_nMaxLength);
        memcpy(m_pBuffer, pcsz, sizeof(ELT) * CBufferRefT <ELT> :: m_nSize);
        m_pBuffer[CBufferRefT <ELT> :: m_nSize] = 0;
    }

    template <class ELT> CBufferT <ELT> :: CBufferT() : CBufferRefT <ELT> (0, 0)
    {
        m_nMaxLength = 0;
        m_pBuffer    = 0;
    }

    template <class ELT> inline ELT & CBufferT <ELT> :: operator [] (int nIndex)
    {
        return m_pBuffer[nIndex];
    }

    template <class ELT> inline const ELT & CBufferT <ELT> :: operator [] (int nIndex) const
    {
        return m_pBuffer[nIndex];
    }

    template <class ELT> void CBufferT <ELT> :: Append(const ELT * pcsz, int length, int eol)
    {
        int nNewLength = m_nMaxLength;

        // Check length
        if(nNewLength < 8)
            nNewLength = 8;

        if(CBufferRefT <ELT> :: m_nSize + length + eol > nNewLength)
            nNewLength *= 2;

        if(CBufferRefT <ELT> :: m_nSize + length + eol > nNewLength)
        {
            nNewLength  = CBufferRefT <ELT> :: m_nSize + length + eol + 11;
            nNewLength -= nNewLength % 8;
        }

        // Realloc
        if(nNewLength > m_nMaxLength)
        {
            CBufferRefT <ELT> :: m_pRef = m_pBuffer = (ELT *) realloc(m_pBuffer, sizeof(ELT) * nNewLength);
            m_nMaxLength = nNewLength;
        }

        // Append
        memcpy(m_pBuffer + CBufferRefT <ELT> :: m_nSize, pcsz, sizeof(ELT) * length);
        CBufferRefT <ELT> :: m_nSize += length;

        if(eol > 0) m_pBuffer[CBufferRefT <ELT> :: m_nSize] = 0;
    }

    template <class ELT> inline void CBufferT <ELT> :: Append(ELT el, int eol)
    {
        Append(&el, 1, eol);
    }

    template <class ELT> void CBufferT <ELT> :: Push(ELT el)
    {
        // Realloc
        if(CBufferRefT <ELT> :: m_nSize >= m_nMaxLength)
        {
            int nNewLength = m_nMaxLength * 2;
            if( nNewLength < 8 ) nNewLength = 8;

            CBufferRefT <ELT> :: m_pRef = m_pBuffer = (ELT *) realloc(m_pBuffer, sizeof(ELT) * nNewLength);
            m_nMaxLength = nNewLength;
        }

        // Append
        m_pBuffer[CBufferRefT <ELT> :: m_nSize++] = el;
    }

    template <class ELT> inline int CBufferT <ELT> :: Pop(ELT & el)
    {
        if(CBufferRefT <ELT> :: m_nSize > 0)
        {
            el = m_pBuffer[--CBufferRefT <ELT> :: m_nSize];
            return 1;
        }
        else
        {
            return 0;
        }
    }

    template <class ELT> inline int CBufferT <ELT> :: Peek(ELT & el) const
    {
        if(CBufferRefT <ELT> :: m_nSize > 0)
        {
            el = m_pBuffer[CBufferRefT <ELT> :: m_nSize - 1];
            return 1;
        }
        else
        {
            return 0;
        }
    }

    template <class ELT> const ELT * CBufferT <ELT> :: GetBuffer() const
    {
        static const ELT _def[] = {0}; return m_pBuffer ? m_pBuffer : _def;
    }

    template <class ELT> ELT * CBufferT <ELT> :: GetBuffer()
    {
        static const ELT _def[] = {0}; return m_pBuffer ? m_pBuffer : (ELT *)_def;
    }

    template <class ELT> ELT * CBufferT <ELT> :: Detach()
    {
        ELT * pBuffer = m_pBuffer;

        CBufferRefT <ELT> :: m_pRef  = m_pBuffer    = 0;
        CBufferRefT <ELT> :: m_nSize = m_nMaxLength = 0;

        return pBuffer;
    }

    template <class ELT> void CBufferT <ELT> :: Release()
    {
        ELT * pBuffer = Detach();

        if(pBuffer != 0) free(pBuffer);
    }

    template <class ELT> void CBufferT <ELT> :: Prepare(int index, int fill)
    {
        int nNewSize = index + 1;

        // Realloc
        if(nNewSize > m_nMaxLength)
        {
            int nNewLength = m_nMaxLength;

            if( nNewLength < 8 )
                nNewLength = 8;

            if( nNewSize > nNewLength )
                nNewLength *= 2;

            if( nNewSize > nNewLength )
            {
                nNewLength  = nNewSize + 11;
                nNewLength -= nNewLength % 8;
            }

            CBufferRefT <ELT> :: m_pRef = m_pBuffer = (ELT *) realloc(m_pBuffer, sizeof(ELT) * nNewLength);
            m_nMaxLength = nNewLength;
        }

        // size
        if( CBufferRefT <ELT> :: m_nSize < nNewSize )
        {
            memset(m_pBuffer + CBufferRefT <ELT> :: m_nSize, fill, sizeof(ELT) * (nNewSize - CBufferRefT <ELT> :: m_nSize));
            CBufferRefT <ELT> :: m_nSize = nNewSize;
        }
    }

    template <class ELT> inline void CBufferT <ELT> :: Restore(int size)
    {
        CBufferRefT <ELT> :: m_nSize = size;
    }

    template <class ELT> CBufferT <ELT> :: ~CBufferT()
    {
        if(m_pBuffer != 0) free(m_pBuffer);
    }

    //
    // Context
    //
    class CContext
    {
    public:
        CBufferT <int> m_stack;
        CBufferT <int> m_capturestack, m_captureindex;

    public:
        int    m_nCurrentPos;
        int    m_nBeginPos;
        int    m_nLastBeginPos;
        int    m_nParenZindex;

        void * m_pMatchString;
        int    m_pMatchStringLength;
    };

    //
    // Interface
    //
    class ElxInterface
    {
    public:
        virtual int Match    (CContext * pContext) const = 0;
        virtual int MatchNext(CContext * pContext) const = 0;

    public:
        virtual ~ElxInterface() {};
    };

    //
    // Alternative
    //
    template <int x> class CAlternativeElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CAlternativeElxT();

    public:
        CBufferT <ElxInterface *> m_elxlist;
    };

    typedef CAlternativeElxT <0> CAlternativeElx;

    //
    // Assert
    //
    template <int x> class CAssertElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CAssertElxT(ElxInterface * pelx, int byes = 1);

    public:
        ElxInterface * m_pelx;
        int m_byes;
    };

    typedef CAssertElxT <0> CAssertElx;

    //
    // Back reference elx
    //
    template <class CHART> class CBackrefElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CBackrefElxT(int nnumber, int brightleft, int bignorecase);

    public:
        int m_nnumber;
        int m_brightleft;
        int m_bignorecase;

        CBufferT <CHART> m_szNamed;
    };

    //
    // Implementation
    //
    template <class CHART> CBackrefElxT <CHART> :: CBackrefElxT(int nnumber, int brightleft, int bignorecase)
    {
        m_nnumber     = nnumber;
        m_brightleft  = brightleft;
        m_bignorecase = bignorecase;
    }

    template <class CHART> int CBackrefElxT <CHART> :: Match(CContext * pContext) const
    {
        // check number, for named
        if( m_nnumber < 0 || m_nnumber >= pContext->m_captureindex.GetSize() ) return 0;

        int index = pContext->m_captureindex[m_nnumber];
        if( index < 0 ) return 0;

        // check enclosed
        int pos1 = pContext->m_capturestack[index + 1];
        int pos2 = pContext->m_capturestack[index + 2];

        if( pos2 < 0 ) pos2 = pContext->m_nCurrentPos;

        // info
        int lpos = pos1 < pos2 ? pos1 : pos2;
        int rpos = pos1 < pos2 ? pos2 : pos1;
        int slen = rpos - lpos;

        const CHART * pcsz = (const CHART *)pContext->m_pMatchString;
        int npos = pContext->m_nCurrentPos;
        int tlen = pContext->m_pMatchStringLength;

        // compare
        int bsucc;
        CBufferRefT <CHART> refstr(pcsz + lpos, slen);

        if( m_brightleft )
        {
            if(npos < slen)
                return 0;

            if(m_bignorecase)
                bsucc = ! refstr.nCompareNoCase(pcsz + (npos - slen));
            else
                bsucc = ! refstr.nCompare      (pcsz + (npos - slen));

            if( bsucc )
            {
                pContext->m_stack.Push(npos);
                pContext->m_nCurrentPos -= slen;
            }
        }
        else
        {
            if(npos + slen > tlen)
                return 0;

            if(m_bignorecase)
                bsucc = ! refstr.nCompareNoCase(pcsz + npos);
            else
                bsucc = ! refstr.nCompare      (pcsz + npos);

            if( bsucc )
            {
                pContext->m_stack.Push(npos);
                pContext->m_nCurrentPos += slen;
            }
        }

        return bsucc;
    }

    template <class CHART> int CBackrefElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        int npos = 0;

        pContext->m_stack.Pop(npos);
        pContext->m_nCurrentPos = npos;

        return 0;
    }

    // RCHART
#ifndef RCHART
#define RCHART(ch) ((CHART)ch)
#endif

    // BOUNDARY_TYPE
    enum BOUNDARY_TYPE
    {
        BOUNDARY_FILE_BEGIN, // begin of whole text
        BOUNDARY_FILE_END  , // end of whole text
        BOUNDARY_FILE_END_N, // end of whole text, or before newline at the end
        BOUNDARY_LINE_BEGIN, // begin of line
        BOUNDARY_LINE_END  , // end of line
        BOUNDARY_WORD_BEGIN, // begin of word
        BOUNDARY_WORD_END  , // end of word
        BOUNDARY_WORD_EDGE
    };

    //
    // Boundary Elx
    //
    template <class CHART> class CBoundaryElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CBoundaryElxT(int ntype, int byes = 1);

    protected:
        static int IsWordChar(CHART ch);

    public:
        int m_ntype;
        int m_byes;
    };

    //
    // Implementation
    //
    template <class CHART> CBoundaryElxT <CHART> :: CBoundaryElxT(int ntype, int byes)
    {
        m_ntype = ntype;
        m_byes  = byes;
    }

    template <class CHART> int CBoundaryElxT <CHART> :: Match(CContext * pContext) const
    {
        const CHART * pcsz  = (const CHART *)pContext->m_pMatchString;
        int npos = pContext->m_nCurrentPos;
        int tlen = pContext->m_pMatchStringLength;

        CHART chL = npos > 0    ? pcsz[npos - 1] : 0;
        CHART chR = npos < tlen ? pcsz[npos    ] : 0;

        int bsucc = 0;

        switch(m_ntype)
        {
        case BOUNDARY_FILE_BEGIN:
            bsucc = (npos <= 0);
            break;

        case BOUNDARY_FILE_END:
            bsucc = (npos >= tlen);
            break;

        case BOUNDARY_FILE_END_N:
            bsucc = (npos >= tlen) || (pcsz[tlen-1] == RCHART('\n') && (npos == tlen-1 || (pcsz[tlen-2] == RCHART('\r') && npos == tlen-2)));
            break;

        case BOUNDARY_LINE_BEGIN:
            bsucc = (npos <= 0   ) || (chL == RCHART('\n')) || ((chL == RCHART('\r')) && (chR != RCHART('\n')));
            break;

        case BOUNDARY_LINE_END:
            bsucc = (npos >= tlen) || (chR == RCHART('\r')) || ((chR == RCHART('\n')) && (chL != RCHART('\r')));
            break;

        case BOUNDARY_WORD_BEGIN:
            bsucc = ! IsWordChar(chL) &&   IsWordChar(chR);
            break;

        case BOUNDARY_WORD_END:
            bsucc =   IsWordChar(chL) && ! IsWordChar(chR);
            break;

        case BOUNDARY_WORD_EDGE:
            bsucc =   IsWordChar(chL) ?  ! IsWordChar(chR) : IsWordChar(chR);
            break;
        }

        return m_byes ? bsucc : ! bsucc;
    }

    template <class CHART> int CBoundaryElxT <CHART> :: MatchNext(CContext *) const
    {
        return 0;
    }

    template <class CHART> inline int CBoundaryElxT <CHART> :: IsWordChar(CHART ch)
    {
        return (ch >= RCHART('A') && ch <= RCHART('Z')) || (ch >= RCHART('a') && ch <= RCHART('z')) || (ch >= RCHART('0') && ch <= RCHART('9')) || (ch == RCHART('_'));
    }

    //
    // Bracket
    //
    template <class CHART> class CBracketElxT : public ElxInterface  
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CBracketElxT(int nnumber, int bright);
        int CheckCaptureIndex(int & index, CContext * pContext) const;

    public:
        int m_nnumber;
        int m_bright;

        CBufferT <CHART> m_szNamed;
    };

    template <class CHART> CBracketElxT <CHART> :: CBracketElxT(int nnumber, int bright)
    {
        m_nnumber = nnumber;
        m_bright  = bright;
    }

    template <class CHART> inline int CBracketElxT <CHART> :: CheckCaptureIndex(int & index, CContext * pContext) const
    {
        if( index >= pContext->m_capturestack.GetSize() )
            index  = pContext->m_capturestack.GetSize() - 4;

        while(index >= 0)
        {
            if(pContext->m_capturestack[index] == m_nnumber)
            {
                return 1;
            }

            index -= 4;
        }


        return 0;
    }

    //
    // capturestack[index+0] => Group number
    // capturestack[index+1] => Capture start pos
    // capturestack[index+2] => Capture end pos
    // capturestack[index+3] => Capture enclose z-index, zindex<0 means inner group with same name
    //
    template <class CHART> int CBracketElxT <CHART> :: Match(CContext * pContext) const
    {
        // check, for named
        if(m_nnumber < 0) return 0;

        if( ! m_bright )
        {
            pContext->m_captureindex.Prepare(m_nnumber, -1);
            int index = pContext->m_captureindex[m_nnumber];

            // check
            if(CheckCaptureIndex(index, pContext) && pContext->m_capturestack[index+2] < 0)
            {
                pContext->m_capturestack[index+3] --;
                return 1;
            }

            // save
            pContext->m_captureindex[m_nnumber] = pContext->m_capturestack.GetSize();

            pContext->m_capturestack.Push(m_nnumber);
            pContext->m_capturestack.Push(pContext->m_nCurrentPos);
            pContext->m_capturestack.Push(-1);
            pContext->m_capturestack.Push( 0); // z-index
        }
        else
        {
            // check
            int index = pContext->m_captureindex[m_nnumber];

            if(CheckCaptureIndex(index, pContext))
            {
                if(pContext->m_capturestack[index + 3] < 0) // check inner group with same name
                {
                    pContext->m_capturestack[index + 3] ++;
                    return 1;
                }

                // save
                pContext->m_capturestack[index + 2] = pContext->m_nCurrentPos;
                pContext->m_capturestack[index + 3] = pContext->m_nParenZindex ++;
            }
        }

        return 1;
    }

    template <class CHART> int CBracketElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        int index = pContext->m_captureindex[m_nnumber];
        if( ! CheckCaptureIndex(index, pContext) )
        {
            return 0;
        }

        if( ! m_bright )
        {
            if(pContext->m_capturestack[index + 3] < 0)
            {
                pContext->m_capturestack[index + 3] ++;
                return 0;
            }

            pContext->m_capturestack.Restore(pContext->m_capturestack.GetSize() - 4);

            // to find
            CheckCaptureIndex(index, pContext);

            // new index
            pContext->m_captureindex[m_nnumber] = index;
        }
        else
        {
            if( pContext->m_capturestack[index + 2] >= 0 )
            {
                pContext->m_capturestack[index + 2] = -1;
                pContext->m_capturestack[index + 3] =  0;
            }
            else
            {
                pContext->m_capturestack[index + 3] --;
            }
        }

        return 0;
    }

    //
    // Deletage
    //
    template <class CHART> class CDelegateElxT : public ElxInterface  
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CDelegateElxT(int ndata = 0);

    public:
        ElxInterface * m_pelx;
        int m_ndata; // +0 : recursive to
        // -3 : named recursive

        CBufferT <CHART> m_szNamed;
    };

    template <class CHART> CDelegateElxT <CHART> :: CDelegateElxT(int ndata)
    {
        m_pelx  = 0;
        m_ndata = ndata;
    }

    template <class CHART> int CDelegateElxT <CHART> :: Match(CContext * pContext) const
    {
        if(m_pelx != 0)
            return m_pelx->Match(pContext);
        else
            return 1;
    }

    template <class CHART> int CDelegateElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        if(m_pelx != 0)
            return m_pelx->MatchNext(pContext);
        else
            return 0;
    }

    //
    // Empty
    //
    template <int x> class CEmptyElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CEmptyElxT();
    };

    typedef CEmptyElxT <0> CEmptyElx;

    //
    // Global
    //
    template <int x> class CGlobalElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CGlobalElxT();
    };

    typedef CGlobalElxT <0> CGlobalElx;

    //
    // Repeat
    //
    template <int x> class CRepeatElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CRepeatElxT(ElxInterface * pelx, int ntimes);

    protected:
        int MatchFixed    (CContext * pContext) const;
        int MatchNextFixed(CContext * pContext) const;

    public:
        ElxInterface * m_pelx;
        int m_nfixed;
    };

    typedef CRepeatElxT <0> CRepeatElx;

    //
    // Greedy
    //
    template <int x> class CGreedyElxT : public CRepeatElxT <x>
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CGreedyElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX);

    protected:
        int MatchVart    (CContext * pContext) const;
        int MatchNextVart(CContext * pContext) const;

    public:
        int m_nvart;
    };

    typedef CGreedyElxT <0> CGreedyElx;

    //
    // Independent
    //
    template <int x> class CIndependentElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CIndependentElxT(ElxInterface * pelx);

    public:
        ElxInterface * m_pelx;
    };

    typedef CIndependentElxT <0> CIndependentElx;

    //
    // List
    //
    template <int x> class CListElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CListElxT(int brightleft);

    public:
        CBufferT <ElxInterface *> m_elxlist;
        int m_brightleft;
    };

    typedef CListElxT <0> CListElx;

    //
    // Posix Elx
    //
    template <class CHART> class CPosixElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CPosixElxT(const char * posix, int brightleft);

    public:
        POSIX_FUNC m_posixfun;
        int m_brightleft;
        int m_byes;
    };

    //
    // Implementation
    //
    template <class CHART> CPosixElxT <CHART> :: CPosixElxT(const char * posix, int brightleft)
    {
        m_brightleft = brightleft;

        if(posix[1] == '^')
        {
            m_byes = 0;
            posix += 2;
        }
        else
        {
            m_byes = 1;
            posix += 1;
        }

        if     (!strncmp(posix, "alnum:", 6)) m_posixfun = ::isalnum ;
        else if(!strncmp(posix, "alpha:", 6)) m_posixfun = ::isalpha ;
        else if(!strncmp(posix, "ascii:", 6)) m_posixfun = ::isascii ;
        else if(!strncmp(posix, "cntrl:", 6)) m_posixfun = ::iscntrl ;
        else if(!strncmp(posix, "digit:", 6)) m_posixfun = ::isdigit ;
        else if(!strncmp(posix, "graph:", 6)) m_posixfun = ::isgraph ;
        else if(!strncmp(posix, "lower:", 6)) m_posixfun = ::islower ;
        else if(!strncmp(posix, "print:", 6)) m_posixfun = ::isprint ;
        else if(!strncmp(posix, "punct:", 6)) m_posixfun = ::ispunct ;
        else if(!strncmp(posix, "space:", 6)) m_posixfun = ::isspace ;
        else if(!strncmp(posix, "upper:", 6)) m_posixfun = ::isupper ;
        else if(!strncmp(posix, "xdigit:",7)) m_posixfun = ::isxdigit;
        else if(!strncmp(posix, "blank:", 6)) m_posixfun = is_blank;
        else                                  m_posixfun = 0         ;
    }

    inline int is_blank(int c)
    {
        return c == 0x20 || c == '\t';
    }

    template <class CHART> int CPosixElxT <CHART> :: Match(CContext * pContext) const
    {
        if(m_posixfun == 0) return 0;

        int tlen = pContext->m_pMatchStringLength;
        int npos = pContext->m_nCurrentPos;

        // check
        int at   = m_brightleft ? npos - 1 : npos;
        if( at < 0 || at >= tlen )
            return 0;

        CHART ch = ((const CHART *)pContext->m_pMatchString)[at];

        int bsucc = (*m_posixfun)(ch);

        if( ! m_byes )
            bsucc = ! bsucc;

        if( bsucc )
            pContext->m_nCurrentPos += m_brightleft ? -1 : 1;

        return bsucc;
    }

    template <class CHART> int CPosixElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        pContext->m_nCurrentPos -= m_brightleft ? -1 : 1;
        return 0;
    }

    //
    // Possessive
    //
    template <int x> class CPossessiveElxT : public CGreedyElxT <x>
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CPossessiveElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX);
    };

    typedef CPossessiveElxT <0> CPossessiveElx;

    //
    // Range Elx
    //
    template <class CHART> class CRangeElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CRangeElxT(int brightleft, int byes);

    public:
        int IsContainChar(CHART ch) const;

    public:
        CBufferT <CHART> m_ranges;
        CBufferT <CHART> m_chars;
        CBufferT <ElxInterface *> m_embeds;

    public:
        int m_brightleft;
        int m_byes;
    };

    //
    // Implementation
    //
    template <class CHART> CRangeElxT <CHART> :: CRangeElxT(int brightleft, int byes)
    {
        m_brightleft = brightleft;
        m_byes       = byes;
    }

    template <class CHART> int CRangeElxT <CHART> :: Match(CContext * pContext) const
    {
        int tlen = pContext->m_pMatchStringLength;
        int npos = pContext->m_nCurrentPos;

        // check
        int at   = m_brightleft ? npos - 1 : npos;
        if( at < 0 || at >= tlen )
            return 0;

        CHART ch = ((const CHART *)pContext->m_pMatchString)[at];
        int bsucc = 0, i;

        // compare
        for(i=0; !bsucc && i<m_ranges.GetSize(); i+=2)
        {
            if(m_ranges[i] <= ch && ch <= m_ranges[i+1]) bsucc = 1;
        }

        for(i=0; !bsucc && i<m_chars.GetSize(); i++)
        {
            if(m_chars[i] == ch) bsucc = 1;
        }

        for(i=0; !bsucc && i<m_embeds.GetSize(); i++)
        {
            if(m_embeds[i]->Match(pContext))
            {
                pContext->m_nCurrentPos = npos;
                bsucc = 1;
            }
        }

        if( ! m_byes )
            bsucc = ! bsucc;

        if( bsucc )
            pContext->m_nCurrentPos += m_brightleft ? -1 : 1;

        return bsucc;
    }

    template <class CHART> int CRangeElxT <CHART> :: IsContainChar(CHART ch) const
    {
        int bsucc = 0, i;

        // compare
        for(i=0; !bsucc && i<m_ranges.GetSize(); i+=2)
        {
            if(m_ranges[i] <= ch && ch <= m_ranges[i+1]) bsucc = 1;
        }

        for(i=0; !bsucc && i<m_chars.GetSize(); i++)
        {
            if(m_chars[i] == ch) bsucc = 1;
        }

        return bsucc;
    }

    template <class CHART> int CRangeElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        pContext->m_nCurrentPos -= m_brightleft ? -1 : 1;
        return 0;
    }

    //
    // Reluctant
    //
    template <int x> class CReluctantElxT : public CRepeatElxT <x>
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CReluctantElxT(ElxInterface * pelx, int nmin = 0, int nmax = INT_MAX);

    protected:
        int MatchVart    (CContext * pContext) const;
        int MatchNextVart(CContext * pContext) const;

    public:
        int m_nvart;
    };

    typedef CReluctantElxT <0> CReluctantElx;

    //
    // String Elx
    //
    template <class CHART> class CStringElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase);

    public:
        CBufferT <CHART> m_szPattern;
        int m_brightleft;
        int m_bignorecase;
    };

    //
    // Implementation
    //
    template <class CHART> CStringElxT <CHART> :: CStringElxT(const CHART * fixed, int nlength, int brightleft, int bignorecase) : m_szPattern(fixed, nlength)
    {
        m_brightleft  = brightleft;
        m_bignorecase = bignorecase;
    }

    template <class CHART> int CStringElxT <CHART> :: Match(CContext * pContext) const
    {
        const CHART * pcsz  = (const CHART *)pContext->m_pMatchString;
        int npos = pContext->m_nCurrentPos;
        int tlen = pContext->m_pMatchStringLength;
        int slen = m_szPattern.GetSize();

        int bsucc;

        if(m_brightleft)
        {
            if(npos < slen)
                return 0;

            if(m_bignorecase)
                bsucc = ! m_szPattern.nCompareNoCase(pcsz + (npos - slen));
            else
                bsucc = ! m_szPattern.nCompare      (pcsz + (npos - slen));

            if( bsucc )
                pContext->m_nCurrentPos -= slen;
        }
        else
        {
            if(npos + slen > tlen)
                return 0;

            if(m_bignorecase)
                bsucc = ! m_szPattern.nCompareNoCase(pcsz + npos);
            else
                bsucc = ! m_szPattern.nCompare      (pcsz + npos);

            if( bsucc )
                pContext->m_nCurrentPos += slen;
        }

        return bsucc;
    }

    template <class CHART> int CStringElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        int slen = m_szPattern.GetSize();

        if(m_brightleft)
            pContext->m_nCurrentPos += slen;
        else
            pContext->m_nCurrentPos -= slen;

        return 0;
    }

    //
    // CConditionElx
    //
    template <class CHART> class CConditionElxT : public ElxInterface
    {
    public:
        int Match    (CContext * pContext) const;
        int MatchNext(CContext * pContext) const;

    public:
        CConditionElxT();

    public:
        // backref condition
        int m_nnumber;
        CBufferT <CHART> m_szNamed;

        // elx condition
        ElxInterface * m_pelxask;

        // selection
        ElxInterface * m_pelxyes, * m_pelxno;
    };

    template <class CHART> CConditionElxT <CHART> :: CConditionElxT()
    {
        m_nnumber = -1;
    }

    template <class CHART> int CConditionElxT <CHART> :: Match(CContext * pContext) const
    {
        // status
        int nbegin = pContext->m_nCurrentPos;
        int nsize  = pContext->m_stack.GetSize();
        int ncsize = pContext->m_capturestack.GetSize();

        // condition result
        int condition_yes = 0;

        // backref type
        if( m_nnumber >= 0 )
        {
            do
            {
                if(m_nnumber >= pContext->m_captureindex.GetSize()) break;

                int index = pContext->m_captureindex[m_nnumber];
                if( index < 0) break;

                // else valid
                condition_yes = 1;
            }
            while(0);
        }
        else
        {
            if( m_pelxask == 0 )
                condition_yes = 1;
            else
                condition_yes = m_pelxask->Match(pContext);

            pContext->m_stack.Restore(nsize);
            pContext->m_nCurrentPos = nbegin;
        }

        // elx result
        int bsucc;
        if( condition_yes )
            bsucc = m_pelxyes == 0 ? 1 : m_pelxyes->Match(pContext);
        else
            bsucc = m_pelxno  == 0 ? 1 : m_pelxno ->Match(pContext);

        if( bsucc )
        {
            pContext->m_stack.Push(ncsize);
            pContext->m_stack.Push(condition_yes);
        }
        else
        {
            pContext->m_capturestack.Restore(ncsize);
        }

        return bsucc;
    }

    template <class CHART> int CConditionElxT <CHART> :: MatchNext(CContext * pContext) const
    {
        // pop
        int ncsize = 0, condition_yes = 0;

        pContext->m_stack.Pop(condition_yes);
        pContext->m_stack.Pop(ncsize);

        // elx result
        int bsucc;
        if( condition_yes )
            bsucc = m_pelxyes == 0 ? 0 : m_pelxyes->MatchNext(pContext);
        else
            bsucc = m_pelxno  == 0 ? 0 : m_pelxno ->MatchNext(pContext);

        if( bsucc )
        {
            pContext->m_stack.Push(ncsize);
            pContext->m_stack.Push(condition_yes);
        }
        else
        {
            pContext->m_capturestack.Restore(ncsize);
        }

        return bsucc;
    }

    //
    // MatchResult
    //
    template <int x> class MatchResultT
    {
    public:
        int IsMatched() const;

    public:
        int GetStart() const;
        int GetEnd  () const;

    public:
        int MaxGroupNumber() const;
        int GetGroupStart(int nGroupNumber) const;
        int GetGroupEnd  (int nGroupNumber) const;

    public:
        MatchResultT(const MatchResultT <x> & from) { *this = from; }
        MatchResultT(CContext * pContext = 0, int nMaxNumber = -1);
        MatchResultT <x> & operator = (const MatchResultT <x> &);
        inline operator int() const { return IsMatched(); }

    public:
        CBufferT <int> m_result;
    };

    typedef MatchResultT <0> MatchResult;

    // Stocked Elx IDs
    enum STOCKELX_ID_DEFINES
    {
        STOCKELX_EMPTY = 0,

        ///////////////////////

        STOCKELX_DOT_ALL,
        STOCKELX_DOT_NOT_ALL,

        STOCKELX_WORD,
        STOCKELX_WORD_NOT,

        STOCKELX_SPACE,
        STOCKELX_SPACE_NOT,

        STOCKELX_DIGITAL,
        STOCKELX_DIGITAL_NOT,

        //////////////////////

        STOCKELX_DOT_ALL_RIGHTLEFT,
        STOCKELX_DOT_NOT_ALL_RIGHTLEFT,

        STOCKELX_WORD_RIGHTLEFT,
        STOCKELX_WORD_RIGHTLEFT_NOT,

        STOCKELX_SPACE_RIGHTLEFT,
        STOCKELX_SPACE_RIGHTLEFT_NOT,

        STOCKELX_DIGITAL_RIGHTLEFT,
        STOCKELX_DIGITAL_RIGHTLEFT_NOT,

        /////////////////////

        STOCKELX_COUNT
    };

    // REGEX_FLAGS
#ifndef _REGEX_FLAGS_DEFINED
    enum REGEX_FLAGS
    {
        NO_FLAG        = 0,
        SINGLELINE     = 0x01,
        MULTILINE      = 0x02,
        GLOBAL         = 0x04,
        IGNORECASE     = 0x08,
        RIGHTTOLEFT    = 0x10,
        EXTENDED       = 0x20
    };
#define _REGEX_FLAGS_DEFINED
#endif

    //
    // Builder T
    //
    template <class CHART> class CBuilderT
    {
    public:
        typedef CDelegateElxT  <CHART> CDelegateElx;
        typedef CBracketElxT   <CHART> CBracketElx;
        typedef CBackrefElxT   <CHART> CBackrefElx;
        typedef CConditionElxT <CHART> CConditionElx;

        // Methods
    public:
        ElxInterface * Build(const CBufferRefT <CHART> & pattern, int flags);
        int GetNamedNumber(const CBufferRefT <CHART> & named) const;
        void Clear();

    public:
        CBuilderT();
        ~CBuilderT();

        // Public Attributes
    public:
        ElxInterface * m_pTopElx;
        int            m_nFlags;
        int            m_nMaxNumber;
        int            m_nNextNamed;
        int            m_nGroupCount;

        CBufferT <ElxInterface  *> m_objlist;
        CBufferT <ElxInterface  *> m_grouplist;
        CBufferT <CDelegateElx  *> m_recursivelist;
        CBufferT <CListElx      *> m_namedlist;
        CBufferT <CBackrefElx   *> m_namedbackreflist;
        CBufferT <CConditionElx *> m_namedconditionlist;

        // CHART_INFO
    protected:
        struct CHART_INFO
        {
        public:
            CHART ch;
            int   type;
            int   pos;
            int   len;

        public:
            CHART_INFO(CHART c, int t, int p = 0, int l = 0) { ch = c; type = t; pos = p; len = l;    }
            inline int operator == (const CHART_INFO & ci)   { return ch == ci.ch && type == ci.type; }
            inline int operator != (const CHART_INFO & ci)   { return ! operator == (ci);             }
        };

    protected:
        static unsigned int Hex2Int(const CHART * pcsz, int length, int & used);
        static int ReadDec(char * & str, unsigned int & dec);
        void MoveNext();
        int  GetNext2();

        ElxInterface * BuildAlternative(int vaflags);
        ElxInterface * BuildList       (int & flags);
        ElxInterface * BuildRepeat     (int & flags);
        ElxInterface * BuildSimple     (int & flags);
        ElxInterface * BuildCharset    (int & flags);
        ElxInterface * BuildRecursive  (int & flags);
        ElxInterface * BuildBoundary   (int & flags);
        ElxInterface * BuildBackref    (int & flags);

        ElxInterface * GetStockElx     (int nStockId);
        ElxInterface * Keep(ElxInterface * pElx);

        // Private Attributes
    protected:
        CBufferRefT <CHART> m_pattern;
        CHART_INFO prev, curr, next, nex2;
        int m_nNextPos;
        int m_nCharsetDepth;
        int m_bQuoted;
        POSIX_FUNC m_quote_fun;

        ElxInterface * m_pStockElxs[STOCKELX_COUNT];
    };

    //
    // Implementation
    //
    template <class CHART> CBuilderT <CHART> :: CBuilderT() : m_pattern(0, 0), prev(0, 0), curr(0, 0), next(0, 0), nex2(0, 0)
    {
        Clear();
    }

    template <class CHART> CBuilderT <CHART> :: ~CBuilderT()
    {
        Clear();
    }

    template <class CHART> int CBuilderT <CHART> :: GetNamedNumber(const CBufferRefT <CHART> & named) const
    {
        for(int i=0; i<m_namedlist.GetSize(); i++)
        {
            if( ! ((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_szNamed.CompareNoCase(named) )
                return ((CBracketElx *)m_namedlist[i]->m_elxlist[0])->m_nnumber;
        }

        return -3;
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: Build(const CBufferRefT <CHART> & pattern, int flags)
    {
        // init
        m_pattern       = pattern;
        m_nNextPos      = 0;
        m_nCharsetDepth = 0;
        m_nMaxNumber    = 0;
        m_nNextNamed    = 0;
        m_nFlags        = flags;
        m_bQuoted       = 0;
        m_quote_fun     = 0;

        m_grouplist         .Restore(0);
        m_recursivelist     .Restore(0);
        m_namedlist         .Restore(0);
        m_namedbackreflist  .Restore(0);
        m_namedconditionlist.Restore(0);

        int i;
        for(i=0; i<3; i++) MoveNext();

        // build
        m_pTopElx = BuildAlternative(flags);

        // group 0
        m_grouplist.Prepare(0);
        m_grouplist[0] = m_pTopElx;

        // append named to unnamed
        m_nGroupCount = m_grouplist.GetSize();

        m_grouplist.Prepare(m_nMaxNumber + m_namedlist.GetSize());

        for(i=0; i<m_namedlist.GetSize(); i++)
        {
            CBracketElx * pleft  = (CBracketElx *)m_namedlist[i]->m_elxlist[0];
            CBracketElx * pright = (CBracketElx *)m_namedlist[i]->m_elxlist[2];

            // append
            m_grouplist[m_nGroupCount ++] = m_namedlist[i];

            if( pleft->m_nnumber > 0 )
                continue;

            // same name
            int find_same_name = GetNamedNumber(pleft->m_szNamed);
            if( find_same_name >= 0 )
            {
                pleft ->m_nnumber = find_same_name;
                pright->m_nnumber = find_same_name;
            }
            else
            {
                m_nMaxNumber ++;

                pleft ->m_nnumber = m_nMaxNumber;
                pright->m_nnumber = m_nMaxNumber;
            }
        }

        for(i=1; i<m_nGroupCount; i++)
        {
            CBracketElx * pleft = (CBracketElx *)((CListElx*)m_grouplist[i])->m_elxlist[0];

            if( pleft->m_nnumber > m_nMaxNumber )
                m_nMaxNumber = pleft->m_nnumber;
        }

        // connect recursive
        for(i=0; i<m_recursivelist.GetSize(); i++)
        {
            if( m_recursivelist[i]->m_ndata == -3 )
                m_recursivelist[i]->m_ndata = GetNamedNumber(m_recursivelist[i]->m_szNamed);

            if( m_recursivelist[i]->m_ndata >= 0 && m_recursivelist[i]->m_ndata <= m_nMaxNumber )
            {
                if( m_recursivelist[i]->m_ndata == 0 )
                    m_recursivelist[i]->m_pelx = m_pTopElx;
                else for(int j=1; j<m_grouplist.GetSize(); j++)
                {
                    if(m_recursivelist[i]->m_ndata == ((CBracketElx *)((CListElx*)m_grouplist[j])->m_elxlist[0])->m_nnumber)
                    {
                        m_recursivelist[i]->m_pelx = m_grouplist[j];
                        break;
                    }
                }
            }
        }

        // named backref
        for(i=0; i<m_namedbackreflist.GetSize(); i++)
        {
            m_namedbackreflist[i]->m_nnumber = GetNamedNumber(m_namedbackreflist[i]->m_szNamed);
        }

        // named condition
        for(i=0; i<m_namedconditionlist.GetSize(); i++)
        {
            int nn = GetNamedNumber(m_namedconditionlist[i]->m_szNamed);
            if( nn >= 0 )
            {
                m_namedconditionlist[i]->m_nnumber = nn;
                m_namedconditionlist[i]->m_pelxask = 0;
            }
        }

        return m_pTopElx;
    }

    template <class CHART> void CBuilderT <CHART> :: Clear()
    {
        for(int i=0; i<m_objlist.GetSize(); i++)
        {
            delete m_objlist[i];
        }

        m_objlist.Restore(0);
        m_pTopElx = 0;
        m_nMaxNumber = 0;

        memset(m_pStockElxs, 0, sizeof(m_pStockElxs));
    }

    //
    // hex to int
    //
    template <class CHART> unsigned int CBuilderT <CHART> :: Hex2Int(const CHART * pcsz, int length, int & used)
    {
        unsigned int result = 0;
        int & i = used;

        for(i=0; i<length; i++)
        {
            if(pcsz[i] >= RCHART('0') && pcsz[i] <= RCHART('9'))
                result = (result << 4) + (pcsz[i] - RCHART('0'));
            else if(pcsz[i] >= RCHART('A') && pcsz[i] <= RCHART('F'))
                result = (result << 4) + (0x0A + (pcsz[i] - RCHART('A')));
            else if(pcsz[i] >= RCHART('a') && pcsz[i] <= RCHART('f'))
                result = (result << 4) + (0x0A + (pcsz[i] - RCHART('a')));
            else
                break;
        }

        return result;
    }

    template <class CHART> inline ElxInterface * CBuilderT <CHART> :: Keep(ElxInterface * pelx)
    {
        m_objlist.Push(pelx);
        return pelx;
    }

    template <class CHART> void CBuilderT <CHART> :: MoveNext()
    {
        // forwards
        prev = curr;
        curr = next;
        next = nex2;

        // get nex2
        while( ! GetNext2() ) {};
    }

    template <class CHART> int CBuilderT <CHART> :: GetNext2()
    {
        // check length
        if(m_nNextPos >= m_pattern.GetSize())
        {
            nex2 = CHART_INFO(0, 1, m_nNextPos, 0);
            return 1;
        }

        int   delta = 1;
        CHART ch    = m_pattern[m_nNextPos];

        // if quoted
        if(m_bQuoted)
        {
            if(ch == RCHART('\\'))
            {
                if(m_pattern[m_nNextPos + 1] == RCHART('E'))
                {
                    m_quote_fun = 0;
                    m_bQuoted   = 0;
                    m_nNextPos += 2;
                    return 0;
                }
            }

            if(m_quote_fun != 0)
                nex2 = CHART_INFO((CHART)(*m_quote_fun)((int)ch), 0, m_nNextPos, delta);
            else
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);

            m_nNextPos += delta;

            return 1;
        }

        // common
        switch(ch)
        {
        case RCHART('\\'):
            {
                CHART ch1 = m_pattern[m_nNextPos+1];

                // backref
                if(ch1 >= RCHART('0') && ch1 <= RCHART('9'))
                {
                    nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
                    break;
                }

                // escape
                delta     = 2;

                switch(ch1)
                {
                case RCHART('A'):
                case RCHART('Z'):
                case RCHART('z'):
                case RCHART('w'):
                case RCHART('W'):
                case RCHART('s'):
                case RCHART('S'):
                case RCHART('B'):
                case RCHART('d'):
                case RCHART('D'):
                case RCHART('k'):
                case RCHART('g'):
                    nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta);
                    break;

                case RCHART('b'):
                    if(m_nCharsetDepth > 0)
                        nex2 = CHART_INFO('\b', 0, m_nNextPos, delta);
                    else
                        nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta);
                    break;

                    /*
                    case RCHART('<'):
                    case RCHART('>'):
                    if(m_nCharsetDepth > 0)
                    nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta);
                    else
                    nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta);
                    break;
                    */

                case RCHART('x'):
                    if(m_pattern[m_nNextPos+2] != '{')
                    {
                        int red = 0;
                        unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 2, red);

                        delta += red;

                        if(red > 0)
                            nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta);
                        else
                            nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta);

                        break;
                    }

                case RCHART('u'):
                    if(m_pattern[m_nNextPos+2] != '{')
                    {
                        int red = 0;
                        unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 2, 4, red);

                        delta += red;

                        if(red > 0)
                            nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta);
                        else
                            nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta);
                    }
                    else
                    {
                        int red = 0;
                        unsigned int ch2 = Hex2Int(m_pattern.GetBuffer() + m_nNextPos + 3, sizeof(int) * 2, red);

                        delta += red;

                        while(m_nNextPos + delta < m_pattern.GetSize() && m_pattern.At(m_nNextPos + delta) != RCHART('}'))
                            delta ++;

                        delta ++; // skip '}'

                        nex2 = CHART_INFO(RCHART(ch2), 0, m_nNextPos, delta);
                    }
                    break;

                case RCHART('a'): nex2 = CHART_INFO(RCHART('\a'), 0, m_nNextPos, delta); break;
                case RCHART('f'): nex2 = CHART_INFO(RCHART('\f'), 0, m_nNextPos, delta); break;
                case RCHART('n'): nex2 = CHART_INFO(RCHART('\n'), 0, m_nNextPos, delta); break;
                case RCHART('r'): nex2 = CHART_INFO(RCHART('\r'), 0, m_nNextPos, delta); break;
                case RCHART('t'): nex2 = CHART_INFO(RCHART('\t'), 0, m_nNextPos, delta); break;
                case RCHART('v'): nex2 = CHART_INFO(RCHART('\v'), 0, m_nNextPos, delta); break;
                case RCHART('e'): nex2 = CHART_INFO(RCHART( 27 ), 0, m_nNextPos, delta); break;

                case RCHART('G'):  // skip '\G'
                    if(m_nCharsetDepth > 0)
                    {
                        m_nNextPos += 2;
                        return 0;
                    }
                    else
                    {
                        nex2 = CHART_INFO(ch1, 1, m_nNextPos, delta);
                        break;
                    }

                case RCHART('L'):
                    if( ! m_quote_fun ) m_quote_fun = ::tolower;

                case RCHART('U'):
                    if( ! m_quote_fun ) m_quote_fun = ::toupper;

                case RCHART('Q'):
                    {
                        m_bQuoted   = 1;
                        m_nNextPos += 2;
                        return 0;
                    }

                case RCHART('E'):
                    {
                        m_quote_fun = 0;
                        m_bQuoted   = 0;
                        m_nNextPos += 2;
                        return 0;
                    }

                case 0:
                    if(m_nNextPos+1 >= m_pattern.GetSize())
                    {
                        delta = 1;
                        nex2 = CHART_INFO(ch , 0, m_nNextPos, delta);
                    }
                    else
                        nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta); // common '\0' char
                    break;

                default:
                    nex2 = CHART_INFO(ch1, 0, m_nNextPos, delta);
                    break;
                }
            }
            break;

        case RCHART('*'):
        case RCHART('+'):
        case RCHART('?'):
        case RCHART('.'):
        case RCHART('{'):
        case RCHART('}'):
        case RCHART(')'):
        case RCHART('|'):
        case RCHART('$'):
            if(m_nCharsetDepth > 0)
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            else
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            break;

        case RCHART('-'):
            if(m_nCharsetDepth > 0)
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            else
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            break;

        case RCHART('('):
            {
                CHART ch1 = m_pattern[m_nNextPos+1];
                CHART ch2 = m_pattern[m_nNextPos+2];

                // skip remark
                if(ch1 == RCHART('?') && ch2 == RCHART('#'))
                {
                    m_nNextPos += 2;
                    while(m_nNextPos < m_pattern.GetSize())
                    {
                        if(m_pattern[m_nNextPos] == RCHART(')'))
                            break;

                        m_nNextPos ++;
                    }

                    if(m_pattern[m_nNextPos] == RCHART(')'))
                    {
                        m_nNextPos ++;

                        // get next nex2
                        return 0;
                    }
                }
                else
                {
                    if(m_nCharsetDepth > 0)
                        nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
                    else
                        nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
                }
            }
            break;

        case RCHART('#'):
            if(m_nFlags & EXTENDED)
            {
                // skip remark
                m_nNextPos ++;

                while(m_nNextPos < m_pattern.GetSize())
                {
                    if(m_pattern[m_nNextPos] == RCHART('\n') || m_pattern[m_nNextPos] == RCHART('\r'))
                        break;

                    m_nNextPos ++;
                }

                // get next nex2
                return 0;
            }
            else
            {
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            }
            break;

        case RCHART(' '):
        case RCHART('\f'):
        case RCHART('\n'):
        case RCHART('\r'):
        case RCHART('\t'):
        case RCHART('\v'):
            if(m_nFlags & EXTENDED)
            {
                m_nNextPos ++;

                // get next nex2
                return 0;
            }
            else
            {
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            }
            break;

        case RCHART('['):
            if( m_nCharsetDepth == 0 || m_pattern.At(m_nNextPos + 1, 0) == RCHART(':') )
            {
                m_nCharsetDepth ++;
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            }
            else
            {
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            }
            break;

        case RCHART(']'):
            if(m_nCharsetDepth > 0)
            {
                m_nCharsetDepth --;
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            }
            else
            {
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            }
            break;

        case RCHART(':'):
            if(next == CHART_INFO(RCHART('['), 1))
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            else
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            break;

        case RCHART('^'):
            if(m_nCharsetDepth == 0 || next == CHART_INFO(RCHART('['), 1) || (curr == CHART_INFO(RCHART('['), 1) && next == CHART_INFO(RCHART(':'), 1)))
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta);
            else
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            break;

        case 0:
            if(m_nNextPos >= m_pattern.GetSize())
                nex2 = CHART_INFO(ch, 1, m_nNextPos, delta); // end of string
            else
                nex2 = CHART_INFO(ch, 0, m_nNextPos, delta); // common '\0' char
            break;

        default:
            nex2 = CHART_INFO(ch, 0, m_nNextPos, delta);
            break;
        }

        m_nNextPos += delta;

        return 1;
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: GetStockElx(int nStockId)
    {
        ElxInterface ** pStockElxs = m_pStockElxs;

        // check
        if(nStockId < 0 || nStockId >= STOCKELX_COUNT)
            return GetStockElx(0);

        // create if no
        if(pStockElxs[nStockId] == 0)
        {
            switch(nStockId)
            {
            case STOCKELX_EMPTY:
                pStockElxs[nStockId] = Keep(new CEmptyElx());
                break;

            case STOCKELX_WORD:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 1));

                    pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z'));
                    pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z'));
                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));
                    pRange->m_chars .Push(RCHART('_'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_WORD_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 0));

                    pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z'));
                    pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z'));
                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));
                    pRange->m_chars .Push(RCHART('_'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DOT_ALL:
                pStockElxs[nStockId] = Keep(new CRangeElxT <CHART> (0, 0));
                break;

            case STOCKELX_DOT_NOT_ALL:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 0));

                    pRange->m_chars .Push(RCHART('\n'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_SPACE:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 1));

                    pRange->m_chars .Push(RCHART(' '));
                    pRange->m_chars .Push(RCHART('\t'));
                    pRange->m_chars .Push(RCHART('\r'));
                    pRange->m_chars .Push(RCHART('\n'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_SPACE_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 0));

                    pRange->m_chars .Push(RCHART(' '));
                    pRange->m_chars .Push(RCHART('\t'));
                    pRange->m_chars .Push(RCHART('\r'));
                    pRange->m_chars .Push(RCHART('\n'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DIGITAL:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 1));

                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DIGITAL_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (0, 0));

                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_WORD_RIGHTLEFT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 1));

                    pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z'));
                    pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z'));
                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));
                    pRange->m_chars .Push(RCHART('_'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_WORD_RIGHTLEFT_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 0));

                    pRange->m_ranges.Push(RCHART('A')); pRange->m_ranges.Push(RCHART('Z'));
                    pRange->m_ranges.Push(RCHART('a')); pRange->m_ranges.Push(RCHART('z'));
                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));
                    pRange->m_chars .Push(RCHART('_'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DOT_ALL_RIGHTLEFT:
                pStockElxs[nStockId] = Keep(new CRangeElxT <CHART> (1, 0));
                break;

            case STOCKELX_DOT_NOT_ALL_RIGHTLEFT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 0));

                    pRange->m_chars .Push(RCHART('\n'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_SPACE_RIGHTLEFT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 1));

                    pRange->m_chars .Push(RCHART(' '));
                    pRange->m_chars .Push(RCHART('\t'));
                    pRange->m_chars .Push(RCHART('\r'));
                    pRange->m_chars .Push(RCHART('\n'));
                    pRange->m_chars .Push(RCHART('\f'));
                    pRange->m_chars .Push(RCHART('\v'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_SPACE_RIGHTLEFT_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 0));

                    pRange->m_chars .Push(RCHART(' '));
                    pRange->m_chars .Push(RCHART('\t'));
                    pRange->m_chars .Push(RCHART('\r'));
                    pRange->m_chars .Push(RCHART('\n'));
                    pRange->m_chars .Push(RCHART('\f'));
                    pRange->m_chars .Push(RCHART('\v'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DIGITAL_RIGHTLEFT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 1));

                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));

                    pStockElxs[nStockId] = pRange;
                }
                break;

            case STOCKELX_DIGITAL_RIGHTLEFT_NOT:
                {
                    CRangeElxT <CHART> * pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (1, 0));

                    pRange->m_ranges.Push(RCHART('0')); pRange->m_ranges.Push(RCHART('9'));

                    pStockElxs[nStockId] = pRange;
                }
                break;
            }
        }

        // return
        return pStockElxs[nStockId];
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildAlternative(int vaflags)
    {
        if(curr == CHART_INFO(0, 1))
            return GetStockElx(STOCKELX_EMPTY);

        // flag instance
        int flags = vaflags;

        // first part
        ElxInterface * pAlternativeOne = BuildList(flags);

        // check alternative
        if(curr == CHART_INFO(RCHART('|'), 1))
        {
            CAlternativeElx * pAlternative = (CAlternativeElx *)Keep(new CAlternativeElx());
            pAlternative->m_elxlist.Push(pAlternativeOne);

            // loop
            while(curr == CHART_INFO(RCHART('|'), 1))
            {
                // skip '|' itself
                MoveNext();

                pAlternativeOne = BuildList(flags);
                pAlternative->m_elxlist.Push(pAlternativeOne);
            }

            return pAlternative;
        }

        return pAlternativeOne;
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildList(int & flags)
    {
        if(curr == CHART_INFO(0, 1) || curr == CHART_INFO(RCHART('|'), 1) || curr == CHART_INFO(RCHART(')'), 1))
            return GetStockElx(STOCKELX_EMPTY);

        // first
        ElxInterface * pListOne = BuildRepeat(flags);

        if(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1))
        {
            CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT));
            pList->m_elxlist.Push(pListOne);

            while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('|'), 1) && curr != CHART_INFO(RCHART(')'), 1))
            {
                pListOne = BuildRepeat(flags);

                // add
                pList->m_elxlist.Push(pListOne);
            }

            return pList;
        }

        return pListOne;
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildRepeat(int & flags)
    {
        // simple
        ElxInterface * pSimple = BuildSimple(flags);

        if(curr.type == 0) return pSimple;

        // is quantifier or not
        int bIsQuantifier = 1;

        // quantifier range
        unsigned int nMin = 0, nMax = 0;

        switch(curr.ch)
        {
        case RCHART('{'):
            {
                CBufferT <char> re;

                // skip '{'
                MoveNext();

                // copy
                while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART('}'), 1))
                {
                    re.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                    MoveNext();
                }

                // skip '}'
                MoveNext();

                // read
                int red;
                char * str = re.GetBuffer();

                if( ! ReadDec(str, nMin) )
                    red = 0;
                else if( *str != ',' )
                    red = 1;
                else
                {
                    str ++;

                    if( ! ReadDec(str, nMax) )
                        red = 2;
                    else
                        red = 3;
                }

                // check
                if(red  <=  1 ) nMax = nMin;
                if(red  ==  2 ) nMax = INT_MAX;
                if(nMax < nMin) nMax = nMin;
            }
            break;

        case RCHART('?'):
            nMin = 0;
            nMax = 1;

            // skip '?'
            MoveNext();
            break;

        case RCHART('*'):
            nMin = 0;
            nMax = INT_MAX;

            // skip '*'
            MoveNext();
            break;

        case RCHART('+'):
            nMin = 1;
            nMax = INT_MAX;

            // skip '+'
            MoveNext();
            break;

        default:
            bIsQuantifier = 0;
            break;
        }

        // do quantify
        if(bIsQuantifier)
        {
            // 0 times
            if(nMax == 0)
                return GetStockElx(STOCKELX_EMPTY);

            // fixed times
            if(nMin == nMax)
            {
                if(curr == CHART_INFO(RCHART('?'), 1) || curr == CHART_INFO(RCHART('+'), 1))
                    MoveNext();

                return Keep(new CRepeatElx(pSimple, nMin));
            }

            // range times
            if(curr == CHART_INFO(RCHART('?'), 1))
            {
                MoveNext();
                return Keep(new CReluctantElx(pSimple, nMin, nMax));
            }
            else if(curr == CHART_INFO(RCHART('+'), 1))
            {
                MoveNext();
                return Keep(new CPossessiveElx(pSimple, nMin, nMax));
            }
            else
            {
                return Keep(new CGreedyElx(pSimple, nMin, nMax));
            }
        }

        return pSimple;
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildSimple(int & flags)
    {
        CBufferT <CHART> fixed;

        while(curr != CHART_INFO(0, 1))
        {
            if(curr.type == 0)
            {
                if(next == CHART_INFO(RCHART('{'), 1) || next == CHART_INFO(RCHART('?'), 1) || next == CHART_INFO(RCHART('*'), 1) || next == CHART_INFO(RCHART('+'), 1))
                {
                    if(fixed.GetSize() == 0)
                    {
                        fixed.Append(curr.ch, 1);
                        MoveNext();
                    }

                    break;
                }
                else
                {
                    fixed.Append(curr.ch, 1);
                    MoveNext();
                }
            }
            else if(curr.type == 1)
            {
                CHART vch = curr.ch;

                // end of simple
                if(vch == RCHART(')') || vch == RCHART('|'))
                    break;

                // has fixed already
                if(fixed.GetSize() > 0)
                    break;

                // left parentheses
                if(vch == RCHART('('))
                {
                    return BuildRecursive(flags);
                }

                // char set
                if( vch == RCHART('[') || vch == RCHART('.') || vch == RCHART('w') || vch == RCHART('W') ||
                    vch == RCHART('s') || vch == RCHART('S') || vch == RCHART('d') || vch == RCHART('D')
                    )
                {
                    return BuildCharset(flags);
                }

                // boundary
                if( vch == RCHART('^') || vch == RCHART('$') || vch == RCHART('A') || vch == RCHART('Z') || vch == RCHART('z') ||
                    vch == RCHART('b') || vch == RCHART('B') || vch == RCHART('G') // vch == RCHART('<') || vch == RCHART('>')
                    )
                {
                    return BuildBoundary(flags);
                }

                // backref
                if(vch == RCHART('\\') || vch == RCHART('k') || vch == RCHART('g'))
                {
                    return BuildBackref(flags);
                }

                // treat vchar as char
                fixed.Append(curr.ch, 1);
                MoveNext();
            }
        }

        if(fixed.GetSize() > 0)
            return Keep(new CStringElxT <CHART> (fixed.GetBuffer(), fixed.GetSize(), flags & RIGHTTOLEFT, flags & IGNORECASE));
        else
            return GetStockElx(STOCKELX_EMPTY);
    }

#define deelx_max(a, b)  (((a) > (b)) ? (a) : (b))
#define deelx_min(a, b)  (((a) < (b)) ? (a) : (b))

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildCharset(int & flags)
    {
        // char
        CHART ch = curr.ch;

        // skip
        MoveNext();

        switch(ch)
        {
        case RCHART('.'):
            return GetStockElx(
                flags & RIGHTTOLEFT ?
                ((flags & SINGLELINE) ? STOCKELX_DOT_ALL_RIGHTLEFT : STOCKELX_DOT_NOT_ALL_RIGHTLEFT) :
                ((flags & SINGLELINE) ? STOCKELX_DOT_ALL : STOCKELX_DOT_NOT_ALL)
                );

        case RCHART('w'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT : STOCKELX_WORD);

        case RCHART('W'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_WORD_RIGHTLEFT_NOT : STOCKELX_WORD_NOT);

        case RCHART('s'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT : STOCKELX_SPACE);

        case RCHART('S'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_SPACE_RIGHTLEFT_NOT : STOCKELX_SPACE_NOT);

        case RCHART('d'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT : STOCKELX_DIGITAL);

        case RCHART('D'):
            return GetStockElx(flags & RIGHTTOLEFT ? STOCKELX_DIGITAL_RIGHTLEFT_NOT : STOCKELX_DIGITAL_NOT);

        case RCHART('['):
            {
                CRangeElxT <CHART> * pRange;

                // create
                if(curr == CHART_INFO(RCHART(':'), 1))
                {
                    CBufferT <char> posix;

                    do {
                        posix.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                        MoveNext();
                    }
                    while(curr.ch != RCHART(0) && curr != CHART_INFO(RCHART(']'), 1));

                    MoveNext(); // skip ']'

                    // posix
                    return Keep(new CPosixElxT <CHART> (posix.GetBuffer(), flags & RIGHTTOLEFT));
                }
                else if(curr == CHART_INFO(RCHART('^'), 1))
                {
                    MoveNext(); // skip '^'
                    pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (flags & RIGHTTOLEFT, 0));
                }
                else
                {
                    pRange = (CRangeElxT <CHART> *)Keep(new CRangeElxT <CHART> (flags & RIGHTTOLEFT, 1));
                }

                // parse
                while(curr != CHART_INFO(0, 1) && curr != CHART_INFO(RCHART(']'), 1))
                {
                    ch = curr.ch;

                    if(curr.type == 1 && (
                        ch == RCHART('.') || ch == RCHART('w') || ch == RCHART('W') || ch == RCHART('s') || ch == RCHART('S') || ch == RCHART('d') || ch == RCHART('D') ||
                        (ch == RCHART('[') && next == CHART_INFO(RCHART(':'), 1))
                        ))
                    {
                        pRange->m_embeds.Push(BuildCharset(flags));
                    }
                    else if(next == CHART_INFO(RCHART('-'), 1) && nex2.type == 0)
                    {
                        pRange->m_ranges.Push(ch); pRange->m_ranges.Push(nex2.ch);

                        // next
                        MoveNext();
                        MoveNext();
                        MoveNext();
                    }
                    else
                    {
                        pRange->m_chars.Push(ch);

                        // next
                        MoveNext();
                    }
                }

                // skip ']'
                MoveNext();

                if( flags & IGNORECASE )
                {
                    CBufferT <CHART> & ranges = pRange->m_ranges;
                    int i, oldcount = ranges.GetSize() / 2;

                    for(i=0; i<oldcount; i++)
                    {
                        CHART newmin, newmax;

                        if( ranges[i*2] <= RCHART('Z') && ranges[i*2+1] >= RCHART('A') )
                        {
                            newmin = tolower( deelx_max(RCHART('A'), ranges[i*2  ]) );
                            newmax = tolower( deelx_min(RCHART('Z'), ranges[i*2+1]) );

                            if( newmin < ranges[i*2] || newmax > ranges[i*2+1] )
                            {
                                ranges.Push(newmin);
                                ranges.Push(newmax);
                            }
                        }

                        if( ranges[i*2] <= RCHART('z') && ranges[i*2+1] >= RCHART('a') )
                        {
                            newmin = toupper( deelx_max(RCHART('a'), ranges[i*2  ]) );
                            newmax = toupper( deelx_min(RCHART('z'), ranges[i*2+1]) );

                            if( newmin < ranges[i*2] || newmax > ranges[i*2+1] )
                            {
                                ranges.Push(newmin);
                                ranges.Push(newmax);
                            }
                        }
                    }

                    CBufferT <CHART> & chars = pRange->m_chars;
                    oldcount = chars.GetSize();
                    for(i=0; i<oldcount; i++)
                    {
                        if( isupper(chars[i]) && ! pRange->IsContainChar(tolower(chars[i])) )
                            chars.Push(tolower(chars[i]));

                        if( islower(chars[i]) && ! pRange->IsContainChar(toupper(chars[i])) )
                            chars.Push(toupper(chars[i]));
                    }
                }

                return pRange;
            }
        }

        return GetStockElx(STOCKELX_EMPTY);
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildRecursive(int & flags)
    {
        // skip '('
        MoveNext();

        if(curr == CHART_INFO(RCHART('?'), 1))
        {
            ElxInterface * pElx = 0;

            // skip '?'
            MoveNext();

            int bNegative = 0;
            CHART named_end = RCHART('>');

            switch(curr.ch)
            {
            case RCHART('!'):
                bNegative = 1;

            case RCHART('='):
                {
                    MoveNext(); // skip '!' or '='
                    pElx = Keep(new CAssertElx(BuildAlternative(flags & ~RIGHTTOLEFT), !bNegative));
                }
                break;

            case RCHART('<'):
                switch(next.ch)
                {
                case RCHART('!'):
                    bNegative = 1;

                case RCHART('='):
                    MoveNext(); // skip '<'
                    MoveNext(); // skip '!' or '='
                    {
                        pElx = Keep(new CAssertElx(BuildAlternative(flags | RIGHTTOLEFT), !bNegative));
                    }
                    break;

                default: // named group
                    break;
                }
                // break if assertion // else named
                if(pElx != 0) break;

            case RCHART('P'):
                if(curr.ch == RCHART('P')) MoveNext(); // skip 'P'

            case RCHART('\''):
                if     (curr.ch == RCHART('<' )) named_end = RCHART('>' );
                else if(curr.ch == RCHART('\'')) named_end = RCHART('\'');
                MoveNext(); // skip '<' or '\''
                {
                    // named number
                    int nThisBackref = m_nNextNamed ++;

                    CListElx    * pList  = (CListElx    *)Keep(new CListElx(flags & RIGHTTOLEFT));
                    CBracketElx * pleft  = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 1 : 0));
                    CBracketElx * pright = (CBracketElx *)Keep(new CBracketElx(-1, flags & RIGHTTOLEFT ? 0 : 1));

                    // save name
                    CBufferT <CHART> & name = pleft->m_szNamed;
                    CBufferT <char> num;

                    while(curr.ch != RCHART(0) && curr.ch != named_end)
                    {
                        name.Append(curr.ch, 1);
                        num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                        MoveNext();
                    }
                    MoveNext(); // skip '>' or '\''

                    // check <num>
                    unsigned int number;
                    char * str = num.GetBuffer();

                    if( ReadDec(str, number) ? ( *str == '\0') : 0 )
                    {
                        pleft ->m_nnumber = number;
                        pright->m_nnumber = number;

                        name.Release();
                    }

                    // left, center, right
                    pList->m_elxlist.Push(pleft);
                    pList->m_elxlist.Push(BuildAlternative(flags));
                    pList->m_elxlist.Push(pright);

                    // for recursive
                    m_namedlist.Prepare(nThisBackref);
                    m_namedlist[nThisBackref] = pList;

                    pElx = pList;
                }
                break;

            case RCHART('>'):
                {
                    MoveNext(); // skip '>'
                    pElx = Keep(new CIndependentElx(BuildAlternative(flags)));
                }
                break;

            case RCHART('R'):
                MoveNext(); // skip 'R'
                while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space

                if(curr.ch == RCHART('<') || curr.ch == RCHART('\''))
                {
                    named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\'');
                    CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(-3));

                    MoveNext(); // skip '<' or '\\'

                    // save name
                    CBufferT <CHART> & name = pDelegate->m_szNamed;
                    CBufferT <char> num;

                    while(curr.ch != RCHART(0) && curr.ch != named_end)
                    {
                        name.Append(curr.ch, 1);
                        num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                        MoveNext();
                    }
                    MoveNext(); // skip '>' or '\''

                    // check <num>
                    unsigned int number;
                    char * str = num.GetBuffer();

                    if( ReadDec(str, number) ? ( *str == '\0') : 0 )
                    {
                        pDelegate->m_ndata = number;
                        name.Release();
                    }

                    m_recursivelist.Push(pDelegate);
                    pElx = pDelegate;
                }
                else
                {
                    CBufferT <char> rto;
                    while(curr.ch != RCHART(0) && curr.ch != RCHART(')'))
                    {
                        rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                        MoveNext();
                    }

                    unsigned int rtono = 0;
                    char * str = rto.GetBuffer();
                    ReadDec(str, rtono);

                    CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono));

                    m_recursivelist.Push(pDelegate);
                    pElx = pDelegate;
                }
                break;

            case RCHART('('):
                {
                    CConditionElx * pConditionElx = (CConditionElx *)Keep(new CConditionElx());

                    // condition
                    ElxInterface * & pCondition = pConditionElx->m_pelxask;

                    if(next == CHART_INFO(RCHART('?'), 1))
                    {
                        pCondition = BuildRecursive(flags);
                    }
                    else // named, assert or number
                    {
                        MoveNext(); // skip '('
                        int pos0 = curr.pos;

                        // save elx condition
                        pCondition = Keep(new CAssertElx(BuildAlternative(flags), 1));

                        // save name
                        pConditionElx->m_szNamed.Append(m_pattern.GetBuffer() + pos0, curr.pos - pos0, 1);

                        // save number
                        CBufferT <char> numstr;
                        while(pos0 < curr.pos)
                        {
                            CHART ch = m_pattern[pos0];
                            numstr.Append(((ch & (CHART)0xff) == ch) ? (char)ch : 0, 1);
                            pos0 ++;
                        }

                        unsigned int number;
                        char * str = numstr.GetBuffer();

                        // valid group number
                        if( ReadDec(str, number) ? ( *str == '\0') : 0 )
                        {
                            pConditionElx->m_nnumber = number;
                            pCondition = 0;
                        }
                        else // maybe elx, maybe named
                        {
                            pConditionElx->m_nnumber = -1;
                            m_namedconditionlist.Push(pConditionElx);
                        }

                        MoveNext(); // skip ')'
                    }

                    // alternative
                    {
                        int newflags = flags;

                        pConditionElx->m_pelxyes = BuildList(newflags);
                    }

                    if(curr.ch == RCHART('|'))
                    {
                        MoveNext(); // skip '|'

                        pConditionElx->m_pelxno = BuildAlternative(flags);
                    }
                    else
                    {
                        pConditionElx->m_pelxno = 0;
                    }

                    pElx = pConditionElx;
                }
                break;

            default:
                while(curr.ch != RCHART(0) && isspace(curr.ch)) MoveNext(); // skip space

                if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9')) // recursive (?1) => (?R1)
                {
                    CBufferT <char> rto;
                    while(curr.ch != RCHART(0) && curr.ch != RCHART(')'))
                    {
                        rto.Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                        MoveNext();
                    }

                    unsigned int rtono = 0;
                    char * str = rto.GetBuffer();
                    ReadDec(str, rtono);

                    CDelegateElx * pDelegate = (CDelegateElx *)Keep(new CDelegateElx(rtono));

                    m_recursivelist.Push(pDelegate);
                    pElx = pDelegate;
                }
                else
                {
                    // flag
                    int newflags = flags;
                    while(curr != CHART_INFO(0, 1) && curr.ch != RCHART(':') && curr.ch != RCHART(')') && curr != CHART_INFO(RCHART('('), 1))
                    {
                        int tochange = 0;

                        switch(curr.ch)
                        {
                        case RCHART('i'):
                        case RCHART('I'):
                            tochange = IGNORECASE;
                            break;

                        case RCHART('s'):
                        case RCHART('S'):
                            tochange = SINGLELINE;
                            break;

                        case RCHART('m'):
                        case RCHART('M'):
                            tochange = MULTILINE;
                            break;

                        case RCHART('g'):
                        case RCHART('G'):
                            tochange = GLOBAL;
                            break;

                        case RCHART('-'):
                            bNegative = 1;
                            break;
                        }

                        if(bNegative)
                            newflags &= ~tochange;
                        else
                            newflags |=  tochange;

                        // move to next char
                        MoveNext();
                    }

                    if(curr.ch == RCHART(':') || curr == CHART_INFO(RCHART('('), 1))
                    {
                        // skip ':'
                        if(curr.ch == RCHART(':')) MoveNext();

                        pElx = BuildAlternative(newflags);
                    }
                    else
                    {
                        // change parent flags
                        flags = newflags;

                        pElx = GetStockElx(STOCKELX_EMPTY);
                    }
                }
                break;
            }

            MoveNext(); // skip ')'

            return pElx;
        }
        else
        {
            // group and number
            CListElx * pList = (CListElx *)Keep(new CListElx(flags & RIGHTTOLEFT));
            int nThisBackref = ++ m_nMaxNumber;

            // left, center, right
            pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 1 : 0)));
            pList->m_elxlist.Push(BuildAlternative(flags));
            pList->m_elxlist.Push(Keep(new CBracketElx(nThisBackref, flags & RIGHTTOLEFT ? 0 : 1)));

            // for recursive
            m_grouplist.Prepare(nThisBackref);
            m_grouplist[nThisBackref] = pList;

            // right
            MoveNext(); // skip ')' 

            return pList;
        }
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildBoundary(int & flags)
    {
        // char
        CHART ch = curr.ch;

        // skip
        MoveNext();

        switch(ch)
        {
        case RCHART('^'):
            return Keep(new CBoundaryElxT <CHART> ((flags & MULTILINE) ? BOUNDARY_LINE_BEGIN : BOUNDARY_FILE_BEGIN));

        case RCHART('$'):
            return Keep(new CBoundaryElxT <CHART> ((flags & MULTILINE) ? BOUNDARY_LINE_END : BOUNDARY_FILE_END));

        case RCHART('b'):
            return Keep(new CBoundaryElxT <CHART> (BOUNDARY_WORD_EDGE));

        case RCHART('B'):
            return Keep(new CBoundaryElxT <CHART> (BOUNDARY_WORD_EDGE, 0));

        case RCHART('A'):
            return Keep(new CBoundaryElxT <CHART> (BOUNDARY_FILE_BEGIN));

        case RCHART('Z'):
            return Keep(new CBoundaryElxT <CHART> (BOUNDARY_FILE_END_N));

        case RCHART('z'):
            return Keep(new CBoundaryElxT <CHART> (BOUNDARY_FILE_END));

        case RCHART('G'):
            if(flags & GLOBAL)
                return Keep(new CGlobalElx());
            else
                return GetStockElx(STOCKELX_EMPTY);

        default:
            return GetStockElx(STOCKELX_EMPTY);
        }
    }

    template <class CHART> ElxInterface * CBuilderT <CHART> :: BuildBackref(int & flags)
    {
        // skip '\\' or '\k' or '\g'
        MoveNext();

        if(curr.ch == RCHART('<') || curr.ch == RCHART('\''))
        {
            CHART named_end = curr.ch == RCHART('<') ? RCHART('>') : RCHART('\'');
            CBackrefElxT <CHART> * pbackref = (CBackrefElxT <CHART> *)Keep(new CBackrefElxT <CHART> (-1, flags & RIGHTTOLEFT, flags & IGNORECASE));

            MoveNext(); // skip '<' or '\''

            // save name
            CBufferT <CHART> & name = pbackref->m_szNamed;
            CBufferT <char> num;

            while(curr.ch != RCHART(0) && curr.ch != named_end)
            {
                name.Append(curr.ch, 1);
                num .Append(((curr.ch & (CHART)0xff) == curr.ch) ? (char)curr.ch : 0, 1);
                MoveNext();
            }
            MoveNext(); // skip '>' or '\''

            // check <num>
            unsigned int number;
            char * str = num.GetBuffer();

            if( ReadDec(str, number) ? ( *str == '\0') : 0 )
            {
                pbackref->m_nnumber = number;
                name.Release();
            }
            else
            {
                m_namedbackreflist.Push(pbackref);
            }

            return pbackref;
        }
        else
        {
            unsigned int nbackref = 0;

            for(int i=0; i<3; i++)
            {
                if(curr.ch >= RCHART('0') && curr.ch <= RCHART('9'))
                    nbackref = nbackref * 10 + (curr.ch - RCHART('0'));
                else
                    break;

                MoveNext();
            }

            return Keep(new CBackrefElxT <CHART> (nbackref, flags & RIGHTTOLEFT, flags & IGNORECASE));
        }
    }

    template <class CHART> int CBuilderT <CHART> :: ReadDec(char * & str, unsigned int & dec)
    {
        int s = 0;
        while(str[s] != 0 && isspace(str[s])) s++;

        if(str[s] < '0' || str[s] > '9') return 0;

        dec = 0;
        unsigned int i;

        for(i = s; i<sizeof(CHART)*3 + s; i++)
        {
            if(str[i] >= '0' && str[i] <= '9')
                dec = dec * 10 + (str[i] - '0');
            else
                break;
        }

        while(str[i] != 0 && isspace(str[i])) i++;
        str += i;

        return 1;
    }

    //
    // Regexp
    //
    template <class CHART> class CRegexpT
    {
    public:
        CRegexpT(const CHART * pattern = 0, int flags = 0);
        CRegexpT(const CHART * pattern, int length, int flags);
        void Compile(const CHART * pattern, int flags = 0);
        void Compile(const CHART * pattern, int length, int flags);

    public:
        MatchResult MatchExact(const CHART * tstring, CContext * pContext = 0) const;
        MatchResult MatchExact(const CHART * tstring, int length, CContext * pContext = 0) const;
        MatchResult Match(const CHART * tstring, int start = -1, CContext * pContext = 0) const;
        MatchResult Match(const CHART * tstring, int length, int start, CContext * pContext = 0) const;
        MatchResult Match(CContext * pContext) const;
        CContext * PrepareMatch(const CHART * tstring, int start = -1, CContext * pContext = 0) const;
        CContext * PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext = 0) const;
        CHART * Replace(const CHART * tstring, const CHART * replaceto, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const;
        CHART * Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start = -1, int ntimes = -1, MatchResult * result = 0, CContext * pContext = 0) const;
        int GetNamedGroupNumber(const CHART * group_name) const;

    public:
        static void ReleaseString (CHART    * tstring );
        static void ReleaseContext(CContext * pContext);

    public:
        CBuilderT <CHART> m_builder;
    };

    //
    // Implementation
    //
    template <class CHART> CRegexpT <CHART> :: CRegexpT(const CHART * pattern, int flags)
    {
        Compile(pattern, CBufferRefT<CHART>(pattern).GetSize(), flags);
    }

    template <class CHART> CRegexpT <CHART> :: CRegexpT(const CHART * pattern, int length, int flags)
    {
        Compile(pattern, length, flags);
    }

    template <class CHART> inline void CRegexpT <CHART> :: Compile(const CHART * pattern, int flags)
    {
        Compile(pattern, CBufferRefT<CHART>(pattern).GetSize(), flags);
    }

    template <class CHART> void CRegexpT <CHART> :: Compile(const CHART * pattern, int length, int flags)
    {
        m_builder.Clear();
        if(pattern != 0) m_builder.Build(CBufferRefT<CHART>(pattern, length), flags);
    }

    template <class CHART> inline MatchResult CRegexpT <CHART> :: MatchExact(const CHART * tstring, CContext * pContext) const
    {
        return MatchExact(tstring, CBufferRefT<CHART>(tstring).GetSize(), pContext);
    }

    template <class CHART> MatchResult CRegexpT <CHART> :: MatchExact(const CHART * tstring, int length, CContext * pContext) const
    {
        if(m_builder.m_pTopElx == 0)
            return 0;

        // info
        int endpos = 0;

        CContext context;
        if(pContext == 0) pContext = &context;

        pContext->m_stack.Restore(0);
        pContext->m_capturestack.Restore(0);
        pContext->m_captureindex.Restore(0);

        pContext->m_nParenZindex  = 0;
        pContext->m_nLastBeginPos = -1;
        pContext->m_pMatchString  = (void*)tstring;
        pContext->m_pMatchStringLength = length;

        if(m_builder.m_nFlags & RIGHTTOLEFT)
        {
            pContext->m_nBeginPos   = length;
            pContext->m_nCurrentPos = length;
            endpos = 0;
        }
        else
        {
            pContext->m_nBeginPos   = 0;
            pContext->m_nCurrentPos = 0;
            endpos = length;
        }

        pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1);
        pContext->m_captureindex[0] = 0;
        pContext->m_capturestack.Push(0);
        pContext->m_capturestack.Push(pContext->m_nCurrentPos);
        pContext->m_capturestack.Push(-1);
        pContext->m_capturestack.Push(-1);

        // match
        if( ! m_builder.m_pTopElx->Match( pContext ) )
            return 0;
        else
        {
            while( pContext->m_nCurrentPos != endpos )
            {
                if( ! m_builder.m_pTopElx->MatchNext( pContext ) )
                    return 0;
                else
                {
                    if( pContext->m_nLastBeginPos == pContext->m_nBeginPos && pContext->m_nBeginPos == pContext->m_nCurrentPos )
                        return 0;
                    else
                        pContext->m_nLastBeginPos = pContext->m_nCurrentPos;
                }
            }

            // end pos
            pContext->m_capturestack[2] = pContext->m_nCurrentPos;

            return MatchResult( pContext, m_builder.m_nMaxNumber );
        }
    }

    template <class CHART> MatchResult CRegexpT <CHART> :: Match(const CHART * tstring, int start, CContext * pContext) const
    {
        return Match(tstring, CBufferRefT<CHART>(tstring).GetSize(), start, pContext);
    }

    template <class CHART> MatchResult CRegexpT <CHART> :: Match(const CHART * tstring, int length, int start, CContext * pContext) const
    {
        if(m_builder.m_pTopElx == 0)
            return 0;

        CContext context;
        if(pContext == 0) pContext = &context;

        PrepareMatch(tstring, length, start, pContext);

        return Match( pContext );
    }

    template <class CHART> MatchResult CRegexpT <CHART> :: Match(CContext * pContext) const
    {
        if(m_builder.m_pTopElx == 0)
            return 0;

        int endpos, delta;

        if(m_builder.m_nFlags & RIGHTTOLEFT)
        {
            endpos = -1;
            delta  = -1;
        }
        else
        {
            endpos = pContext->m_pMatchStringLength + 1;
            delta  = 1;
        }

        while(pContext->m_nCurrentPos != endpos)
        {
            pContext->m_captureindex.Restore(0);
            pContext->m_stack       .Restore(0);
            pContext->m_capturestack.Restore(0);

            pContext->m_captureindex.Prepare(m_builder.m_nMaxNumber, -1);
            pContext->m_captureindex[0] = 0;
            pContext->m_capturestack.Push(0);
            pContext->m_capturestack.Push(pContext->m_nCurrentPos);
            pContext->m_capturestack.Push(-1);
            pContext->m_capturestack.Push(-1);

            if( m_builder.m_pTopElx->Match( pContext ) )
            {
                pContext->m_capturestack[2] = pContext->m_nCurrentPos;

                // zero width
                if( /* pContext->m_nLastBeginPos == pContext->m_nBeginPos && */ pContext->m_nBeginPos == pContext->m_nCurrentPos )
                {
                    pContext->m_nCurrentPos += delta;
                    /* continue; */
                }

                // save pos
                pContext->m_nLastBeginPos   = pContext->m_nBeginPos;
                pContext->m_nBeginPos       = pContext->m_nCurrentPos;

                // return
                return MatchResult( pContext, m_builder.m_nMaxNumber );
            }
            else
            {
                pContext->m_nCurrentPos += delta;
            }
        }

        return 0;
    }

    template <class CHART> inline CContext * CRegexpT <CHART> :: PrepareMatch(const CHART * tstring, int start, CContext * pContext) const
    {
        return PrepareMatch(tstring, CBufferRefT<CHART>(tstring).GetSize(), start, pContext);
    }

    template <class CHART> CContext * CRegexpT <CHART> :: PrepareMatch(const CHART * tstring, int length, int start, CContext * pContext) const
    {
        if(m_builder.m_pTopElx == 0)
            return 0;

        if(pContext == 0) pContext = new CContext();

        pContext->m_nParenZindex  =  0;
        pContext->m_nLastBeginPos = -1;
        pContext->m_pMatchString  = (void*)tstring;
        pContext->m_pMatchStringLength = length;

        if(start < 0)
        {
            if(m_builder.m_nFlags & RIGHTTOLEFT)
            {
                pContext->m_nBeginPos   = length;
                pContext->m_nCurrentPos = length;
            }
            else
            {
                pContext->m_nBeginPos   = 0;
                pContext->m_nCurrentPos = 0;
            }
        }
        else
        {
            if(start > length) start = length + ((m_builder.m_nFlags & RIGHTTOLEFT)?0:1);

            pContext->m_nBeginPos   = start;
            pContext->m_nCurrentPos = start;
        }

        return pContext;
    }

    template <class CHART> inline int CRegexpT <CHART> :: GetNamedGroupNumber(const CHART * group_name) const
    {
        return m_builder.GetNamedNumber(group_name);
    }

    template <class CHART> CHART * CRegexpT <CHART> :: Replace(const CHART * tstring, const CHART * replaceto, int start, int ntimes, MatchResult * result, CContext * pContext) const
    {
        int result_length = 0;
        return Replace(tstring, CBufferRefT<CHART>(tstring).GetSize(), replaceto, CBufferRefT<CHART>(replaceto).GetSize(), result_length, start, ntimes, result, pContext);
    }

    template <class CHART> CHART * CRegexpT <CHART> :: Replace(const CHART * tstring, int string_length, const CHART * replaceto, int to_length, int & result_length, int start, int ntimes, MatchResult * remote_result, CContext * oContext) const
    {
        if(m_builder.m_pTopElx == 0) return 0;

        // --- compile replace to ---

        CBufferT <int> compiledto;

        static const CHART rtoptn[] = { RCHART('\\'), RCHART('$' ), RCHART('('), RCHART('?'), RCHART(':'), RCHART('[' ), RCHART('$' ), RCHART('&' ), RCHART('`' ), RCHART('\''), RCHART('+'), RCHART('_' ), RCHART('\\'), RCHART('d'), RCHART(']'), RCHART('|'), RCHART('\\'), RCHART('{'), RCHART('.'), RCHART('*'), RCHART('?'), RCHART('\\'), RCHART('}'), RCHART(')' ), RCHART('\0') };
        static CRegexpT <CHART> rtoreg(rtoptn);

        MatchResult local_result(0), * result = remote_result ? remote_result : & local_result;

        // prepare
        CContext * pContext = PrepareMatch(replaceto, to_length, -1, oContext);
        int lastIndex = 0, nmatch = 0;

        while( ((*result) = rtoreg.Match(pContext)).IsMatched() )
        {
            int delta = result->GetStart() - lastIndex;
            if( delta > 0 )
            {
                compiledto.Push(lastIndex);
                compiledto.Push(delta);
            }

            lastIndex = result->GetStart();
            delta     = 2;

            switch(replaceto[lastIndex + 1])
            {
            case RCHART('$'):
                compiledto.Push(lastIndex);
                compiledto.Push(1);
                break;

            case RCHART('&'):
            case RCHART('`'):
            case RCHART('\''):
            case RCHART('+'):
            case RCHART('_'):
                compiledto.Push(-1);
                compiledto.Push((int)replaceto[lastIndex + 1]);
                break;

            case RCHART('{'):
                delta  = result->GetEnd() - result->GetStart();
                nmatch = m_builder.GetNamedNumber(CBufferRefT <CHART> (replaceto + (lastIndex + 2), delta - 3));

                if(nmatch > 0 && nmatch <= m_builder.m_nMaxNumber)
                {
                    compiledto.Push(-2);
                    compiledto.Push(nmatch);
                }
                else
                {
                    compiledto.Push(lastIndex);
                    compiledto.Push(delta);
                }
                break;

            default:
                nmatch = 0;
                for(delta=1; delta<=3; delta++)
                {
                    CHART ch = replaceto[lastIndex + delta];

                    if(ch < RCHART('0') || ch > RCHART('9'))
                        break;

                    nmatch = nmatch * 10 + (ch - RCHART('0'));
                }

                if(nmatch > m_builder.m_nMaxNumber)
                {
                    while(nmatch > m_builder.m_nMaxNumber)
                    {
                        nmatch /= 10;
                        delta --;
                    }

                    if(nmatch == 0)
                    {
                        delta = 1;
                    }
                }

                if(delta == 1)
                {
                    compiledto.Push(lastIndex);
                    compiledto.Push(1);
                }
                else
                {
                    compiledto.Push(-2);
                    compiledto.Push(nmatch);
                }
                break;
            }

            lastIndex += delta;
        }

        if(lastIndex < to_length)
        {
            compiledto.Push(lastIndex);
            compiledto.Push(to_length - lastIndex);
        }

        int rightleft = m_builder.m_nFlags & RIGHTTOLEFT;

        int tb = rightleft ? compiledto.GetSize() - 2 : 0;
        int te = rightleft ? -2 : compiledto.GetSize();
        int ts = rightleft ? -2 : 2;

        // --- compile complete ---

        int beginpos  = rightleft ? string_length : 0;
        int endpos    = rightleft ? 0 : string_length;

        int toIndex0  = 0;
        int toIndex1  = 0;
        int i, ntime;

        CBufferT <const CHART *> buffer;

        // prepare
        pContext  = PrepareMatch(tstring, string_length, start, pContext);
        lastIndex = beginpos;

        // Match
        for(ntime = 0; ntimes < 0 || ntime < ntimes; ntime ++)
        {
            (*result) = Match(pContext);

            if( ! result->IsMatched() )
                break;

            // before
            if( rightleft )
            {
                int distance = lastIndex - result->GetEnd();
                if( distance )
                {
                    buffer.Push(tstring + result->GetEnd());
                    buffer.Push((const CHART *)distance);

                    toIndex1 -= distance;
                }
                lastIndex = result->GetStart();
            }
            else
            {
                int distance = result->GetStart() - lastIndex;
                if( distance )
                {
                    buffer.Push(tstring + lastIndex);
                    buffer.Push((const CHART *)distance);

                    toIndex1 += distance;
                }
                lastIndex = result->GetEnd();
            }

            toIndex0 = toIndex1;

            // middle
            for(i=tb; i!=te; i+=ts)
            {
                int off = compiledto[i];
                int len = compiledto[i + 1];

                const CHART * sub = replaceto + off;

                if( off == -1 )
                {
                    switch(RCHART(len))
                    {
                    case RCHART('&'):
                        sub = tstring + result->GetStart();
                        len = result->GetEnd() - result->GetStart();
                        break;

                    case RCHART('`'):
                        sub = tstring;
                        len = result->GetStart();
                        break;

                    case RCHART('\''):
                        sub = tstring + result->GetEnd();
                        len = string_length - result->GetEnd();
                        break;

                    case RCHART('+'):
                        for(nmatch = result->MaxGroupNumber(); nmatch >= 0; nmatch --)
                        {
                            if(result->GetGroupStart(nmatch) >= 0) break;
                        }
                        sub = tstring + result->GetGroupStart(nmatch);
                        len = result->GetGroupEnd(nmatch) - result->GetGroupStart(nmatch);
                        break;

                    case RCHART('_'):
                        sub = tstring;
                        len = string_length;
                        break;
                    }
                }
                else if( off == -2 )
                {
                    sub = tstring + result->GetGroupStart(len);
                    len = result->GetGroupEnd(len) - result->GetGroupStart(len);
                }

                buffer.Push(sub);
                buffer.Push((const CHART *)len);

                toIndex1 += rightleft ? (-len) : len;
            }
        }

        // after
        if(rightleft)
        {
            if(endpos < lastIndex)
            {
                buffer.Push(tstring + endpos);
                buffer.Push((const CHART *)(lastIndex - endpos));
            }
        }
        else
        {
            if(lastIndex < endpos)
            {
                buffer.Push(tstring + lastIndex);
                buffer.Push((const CHART *)(endpos - lastIndex));
            }
        }

        if(oContext == 0) ReleaseContext(pContext);

        // join string
        result_length = 0;
        for(i=0; i<buffer.GetSize(); i+=2)
        {
            result_length += (int)buffer[i+1];
        }

        CBufferT <CHART> result_string;
        result_string.Prepare(result_length);
        result_string.Restore(0);

        if(rightleft)
        {
            for(i=buffer.GetSize()-2; i>=0; i-=2)
            {
                result_string.Append(buffer[i], (int)buffer[i+1]);
            }
        }
        else
        {
            for(i=0; i<buffer.GetSize(); i+=2)
            {
                result_string.Append(buffer[i], (int)buffer[i+1]);
            }
        }

        result_string.Append(0);

        result->m_result.Append(result_length, 3);
        result->m_result.Append(ntime);

        if(rightleft)
        {
            result->m_result.Append(result_length - toIndex1);
            result->m_result.Append(result_length - toIndex0);
        }
        else
        {
            result->m_result.Append(toIndex0);
            result->m_result.Append(toIndex1);
        }

        return result_string.Detach();
    }

    template <class CHART> inline void CRegexpT <CHART> :: ReleaseString(CHART * tstring)
    {
        if(tstring != 0) free(tstring);
    }

    template <class CHART> inline void CRegexpT <CHART> :: ReleaseContext(CContext * pContext)
    {
        if(pContext != 0) delete pContext;
    }

    //
    // All implementations
    //
    template <int x> CAlternativeElxT <x> :: CAlternativeElxT()
    {
    }

    template <int x> int CAlternativeElxT <x> :: Match(CContext * pContext) const
    {
        if(m_elxlist.GetSize() == 0)
            return 1;

        // try all
        for(int n = 0; n < m_elxlist.GetSize(); n++)
        {
            if(m_elxlist[n]->Match(pContext))
            {
                pContext->m_stack.Push(n);
                return 1;
            }
        }

        return 0;
    }

    template <int x> int CAlternativeElxT <x> :: MatchNext(CContext * pContext) const
    {
        if(m_elxlist.GetSize() == 0)
            return 0;

        int n = 0;

        // recall prev
        pContext->m_stack.Pop(n);

        // prev
        if(m_elxlist[n]->MatchNext(pContext))
        {
            pContext->m_stack.Push(n);
            return 1;
        }
        else
        {
            // try rest
            for(n++; n < m_elxlist.GetSize(); n++)
            {
                if(m_elxlist[n]->Match(pContext))
                {
                    pContext->m_stack.Push(n);
                    return 1;
                }
            }

            return 0;
        }
    }

    // assertx.cpp: implementation of the CAssertElx class.
    //
    template <int x> CAssertElxT <x> :: CAssertElxT(ElxInterface * pelx, int byes)
    {
        m_pelx = pelx;
        m_byes = byes;
    }

    template <int x> int CAssertElxT <x> :: Match(CContext * pContext) const
    {
        int nbegin = pContext->m_nCurrentPos;
        int nsize  = pContext->m_stack.GetSize();
        int ncsize = pContext->m_capturestack.GetSize();
        int bsucc;

        // match
        if( m_byes )
            bsucc =   m_pelx->Match(pContext);
        else
            bsucc = ! m_pelx->Match(pContext);

        // status
        pContext->m_stack.Restore(nsize);
        pContext->m_nCurrentPos = nbegin;

        if( bsucc )
            pContext->m_stack.Push(ncsize);
        else
            pContext->m_capturestack.Restore(ncsize);

        return bsucc;
    }

    template <int x> int CAssertElxT <x> :: MatchNext(CContext * pContext) const
    {
        int ncsize = 0;

        pContext->m_stack.Pop(ncsize);
        pContext->m_capturestack.Restore(ncsize);

        return 0;
    }

    // emptyelx.cpp: implementation of the CEmptyElx class.
    //
    template <int x> CEmptyElxT <x> :: CEmptyElxT()
    {
    }

    template <int x> int CEmptyElxT <x> :: Match(CContext *) const
    {
        return 1;
    }

    template <int x> int CEmptyElxT <x> :: MatchNext(CContext *) const
    {
        return 0;
    }

    // globalx.cpp: implementation of the CGlobalElx class.
    //
    template <int x> CGlobalElxT <x> ::CGlobalElxT()
    {
    }

    template <int x> int CGlobalElxT <x> :: Match(CContext * pContext) const
    {
        return pContext->m_nCurrentPos == pContext->m_nBeginPos;
    }

    template <int x> int CGlobalElxT <x> :: MatchNext(CContext *) const
    {
        return 0;
    }

    // greedelx.cpp: implementation of the CGreedyElx class.
    //
    template <int x> CGreedyElxT <x> :: CGreedyElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT <x> (pelx, nmin)
    {
        m_nvart = nmax - nmin;
    }

    template <int x> int CGreedyElxT <x> :: Match(CContext * pContext) const
    {
        if( ! CRepeatElxT <x> :: MatchFixed(pContext) )
            return 0;

        while( ! MatchVart(pContext) )
        {
            if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
                return 0;
        }

        return 1;
    }

    template <int x> int CGreedyElxT <x> :: MatchNext(CContext * pContext) const
    {
        if( MatchNextVart(pContext) )
            return 1;

        if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
            return 0;

        while( ! MatchVart(pContext) )
        {
            if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
                return 0;
        }

        return 1;
    }

    template <int x> int CGreedyElxT <x> :: MatchVart(CContext * pContext) const
    {
        int n      = 0;
        int nbegin = pContext->m_nCurrentPos;

        while(n < m_nvart && CRepeatElxT <x> :: m_pelx->Match(pContext))
        {
            while(pContext->m_nCurrentPos == nbegin)
            {
                if( ! CRepeatElxT <x> :: m_pelx->MatchNext(pContext) ) break;
            }

            if(pContext->m_nCurrentPos == nbegin) break;

            n ++;
            nbegin = pContext->m_nCurrentPos;
        }

        pContext->m_stack.Push(n);

        return 1;
    }

    template <int x> int CGreedyElxT <x> :: MatchNextVart(CContext * pContext) const
    {
        int n = 0;
        pContext->m_stack.Pop(n);

        if(n == 0) return 0;

        if( ! CRepeatElxT <x> :: m_pelx->MatchNext(pContext) )
        {
            n --;
        }

        pContext->m_stack.Push(n);

        return 1;
    }

    // indepelx.cpp: implementation of the CIndependentElx class.
    //
    template <int x> CIndependentElxT <x> :: CIndependentElxT(ElxInterface * pelx)
    {
        m_pelx = pelx;
    }

    template <int x> int CIndependentElxT <x> :: Match(CContext * pContext) const
    {
        int nbegin = pContext->m_nCurrentPos;
        int nsize  = pContext->m_stack.GetSize();
        int ncsize = pContext->m_capturestack.GetSize();

        // match
        int bsucc  = m_pelx->Match(pContext);

        // status
        pContext->m_stack.Restore(nsize);

        if( bsucc )
        {
            pContext->m_stack.Push(nbegin);
            pContext->m_stack.Push(ncsize);
        }

        return bsucc;
    }

    template <int x> int CIndependentElxT <x> :: MatchNext(CContext * pContext) const
    {
        int nbegin = 0, ncsize = 0;

        pContext->m_stack.Pop(ncsize);
        pContext->m_stack.Pop(nbegin);

        pContext->m_capturestack.Restore(ncsize);
        pContext->m_nCurrentPos = nbegin;

        return 0;
    }

    // listelx.cpp: implementation of the CListElx class.
    //
    template <int x> CListElxT <x> :: CListElxT(int brightleft)
    {
        m_brightleft = brightleft;
    }

    template <int x> int CListElxT <x> :: Match(CContext * pContext) const
    {
        if(m_elxlist.GetSize() == 0)
            return 1;

        // prepare
        int bol = m_brightleft ? m_elxlist.GetSize() : -1;
        int stp = m_brightleft ? -1 : 1;
        int eol = m_brightleft ? -1 : m_elxlist.GetSize();

        // from first
        int n = bol + stp;

        // match all
        while(n != eol)
        {
            if(m_elxlist[n]->Match(pContext))
            {
                n += stp;
            }
            else
            {
                n -= stp;

                while(n != bol && ! m_elxlist[n]->MatchNext(pContext))
                    n -= stp;

                if(n != bol)
                    n += stp;
                else
                    return 0;
            }
        }

        return 1;
    }

    template <int x> int CListElxT <x> :: MatchNext(CContext * pContext) const
    {
        if(m_elxlist.GetSize() == 0)
            return 0;

        // prepare
        int bol = m_brightleft ? m_elxlist.GetSize() : -1;
        int stp = m_brightleft ? -1 : 1;
        int eol = m_brightleft ? -1 : m_elxlist.GetSize();

        // from last
        int n = eol - stp;

        while(n != bol && ! m_elxlist[n]->MatchNext(pContext))
            n -= stp;

        if(n != bol)
            n += stp;
        else
            return 0;

        // match rest
        while(n != eol)
        {
            if(m_elxlist[n]->Match(pContext))
            {
                n += stp;
            }
            else
            {
                n -= stp;

                while(n != bol && ! m_elxlist[n]->MatchNext(pContext))
                    n -= stp;

                if(n != bol)
                    n += stp;
                else
                    return 0;
            }
        }

        return 1;
    }

    // mresult.cpp: implementation of the MatchResult class.
    //
    template <int x> MatchResultT <x> :: MatchResultT(CContext * pContext, int nMaxNumber)
    {
        if(pContext != 0)
        {
            m_result.Prepare(nMaxNumber * 2 + 3, -1);

            // matched
            m_result[0] = 1;
            m_result[1] = nMaxNumber;

            for(int n = 0; n <= nMaxNumber; n++)
            {
                int index = pContext->m_captureindex[n];
                if( index < 0 ) continue;

                // check enclosed
                int pos1 = pContext->m_capturestack[index + 1];
                int pos2 = pContext->m_capturestack[index + 2];

                // info
                m_result[n*2 + 2] = pos1 < pos2 ? pos1 : pos2;
                m_result[n*2 + 3] = pos1 < pos2 ? pos2 : pos1;
            }
        }
    }

    template <int x> inline int MatchResultT <x> :: IsMatched() const
    {
        return m_result.At(0, 0);
    }

    template <int x> inline int MatchResultT <x> :: MaxGroupNumber() const
    {
        return m_result.At(1, 0);
    }

    template <int x> inline int MatchResultT <x> :: GetStart() const
    {
        return m_result.At(2, -1);
    }

    template <int x> inline int MatchResultT <x> :: GetEnd() const
    {
        return m_result.At(3, -1);
    }

    template <int x> inline int MatchResultT <x> :: GetGroupStart(int nGroupNumber) const
    {
        return m_result.At(2 + nGroupNumber * 2, -1);
    }

    template <int x> inline int MatchResultT <x> :: GetGroupEnd(int nGroupNumber) const
    {
        return m_result.At(2 + nGroupNumber * 2 + 1, -1);
    }

    template <int x> MatchResultT <x> & MatchResultT <x> :: operator = (const MatchResultT <x> & result)
    {
        m_result.Restore(0);
        if(result.m_result.GetSize() > 0) m_result.Append(result.m_result.GetBuffer(), result.m_result.GetSize());

        return *this;
    }

    // posselx.cpp: implementation of the CPossessiveElx class.
    //
    template <int x> CPossessiveElxT <x> :: CPossessiveElxT(ElxInterface * pelx, int nmin, int nmax) : CGreedyElxT <x> (pelx, nmin, nmax)
    {
    }

    template <int x> int CPossessiveElxT <x> :: Match(CContext * pContext) const
    {
        int nbegin = pContext->m_nCurrentPos;
        int nsize  = pContext->m_stack.GetSize();
        int ncsize = pContext->m_capturestack.GetSize();
        int bsucc  = 1;

        // match
        if( ! CRepeatElxT <x> :: MatchFixed(pContext) )
        {
            bsucc = 0;
        }
        else
        {
            while( ! CGreedyElxT <x> :: MatchVart(pContext) )
            {
                if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
                {
                    bsucc = 0;
                    break;
                }
            }
        }

        // status
        pContext->m_stack.Restore(nsize);

        if( bsucc )
        {
            pContext->m_stack.Push(nbegin);
            pContext->m_stack.Push(ncsize);
        }

        return bsucc;
    }

    template <int x> int CPossessiveElxT <x> :: MatchNext(CContext * pContext) const
    {
        int nbegin = 0, ncsize = 0;

        pContext->m_stack.Pop(ncsize);
        pContext->m_stack.Pop(nbegin);

        pContext->m_capturestack.Restore(ncsize);
        pContext->m_nCurrentPos = nbegin;

        return 0;
    }

    // reluctx.cpp: implementation of the CReluctantElx class.
    //
    template <int x> CReluctantElxT <x> :: CReluctantElxT(ElxInterface * pelx, int nmin, int nmax) : CRepeatElxT <x> (pelx, nmin)
    {
        m_nvart = nmax - nmin;
    }

    template <int x> int CReluctantElxT <x> :: Match(CContext * pContext) const
    {
        if( ! CRepeatElxT <x> :: MatchFixed(pContext) )
            return 0;

        while( ! MatchVart(pContext) )
        {
            if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
                return 0;
        }

        return 1;
    }

    template <int x> int CReluctantElxT <x> :: MatchNext(CContext * pContext) const
    {
        if( MatchNextVart(pContext) )
            return 1;

        if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
            return 0;

        while( ! MatchVart(pContext) )
        {
            if( ! CRepeatElxT <x> :: MatchNextFixed(pContext) )
                return 0;
        }

        return 1;
    }

    template <int x> int CReluctantElxT <x> :: MatchVart(CContext * pContext) const
    {
        pContext->m_stack.Push(0);

        return 1;
    }

    template <int x> int CReluctantElxT <x> :: MatchNextVart(CContext * pContext) const
    {
        int n = 0, nbegin = pContext->m_nCurrentPos;

        pContext->m_stack.Pop(n);

        if(n < m_nvart && CRepeatElxT <x> :: m_pelx->Match(pContext))
        {
            while(pContext->m_nCurrentPos == nbegin)
            {
                if( ! CRepeatElxT <x> :: m_pelx->MatchNext(pContext) ) break;
            }

            if(pContext->m_nCurrentPos != nbegin)
            {
                n ++;

                pContext->m_stack.Push(nbegin);
                pContext->m_stack.Push(n);

                return 1;
            }
        }

        while(n > 0)
        {
            pContext->m_stack.Pop(nbegin);

            while( CRepeatElxT <x> :: m_pelx->MatchNext(pContext) )
            {
                if(pContext->m_nCurrentPos != nbegin)
                {
                    pContext->m_stack.Push(nbegin);
                    pContext->m_stack.Push(n);

                    return 1;
                }
            }

            n --;
        }

        return 0;
    }

    // repeatx.cpp: implementation of the CRepeatElx class.
    //
    template <int x> CRepeatElxT <x> :: CRepeatElxT(ElxInterface * pelx, int ntimes)
    {
        m_pelx   = pelx;
        m_nfixed = ntimes;
    }

    template <int x> int CRepeatElxT <x> :: Match(CContext * pContext) const
    {
        return MatchFixed(pContext);
    }

    template <int x> int CRepeatElxT <x> :: MatchNext(CContext * pContext) const
    {
        return MatchNextFixed(pContext);
    }

    template <int x> int CRepeatElxT <x> :: MatchFixed(CContext * pContext) const
    {
        if(m_nfixed == 0)
            return 1;

        int n = 0;

        while(n < m_nfixed)
        {
            if(m_pelx->Match(pContext))
            {
                n ++;
            }
            else
            {
                n --;

                while(n >= 0 && ! m_pelx->MatchNext(pContext))
                    n --;

                if(n >= 0)
                    n ++;
                else
                    return 0;
            }
        }

        return 1;
    }

    template <int x> int CRepeatElxT <x> :: MatchNextFixed(CContext * pContext) const
    {
        if(m_nfixed == 0)
            return 0;

        // from last
        int n = m_nfixed - 1;

        while(n >= 0 && ! m_pelx->MatchNext(pContext))
            n --;

        if(n >= 0)
            n ++;
        else
            return 0;

        // match rest
        while(n < m_nfixed)
        {
            if(m_pelx->Match(pContext))
            {
                n ++;
            }
            else
            {
                n --;

                while(n >= 0 && ! m_pelx->MatchNext(pContext))
                    n --;

                if(n >= 0)
                    n ++;
                else
                    return 0;
            }
        }

        return 1;
    }

    // Regexp
    typedef CRegexpT <char> CRegexpA;
    typedef CRegexpT <unsigned short> CRegexpW;

#if defined(_UNICODE) || defined(UNICODE)
    typedef CRegexpW CRegexp;
#else
    typedef CRegexpA CRegexp;
#endif

} // xos_stl

#endif//__XOSKIT_XOS_CORE_XOS_STL_SRC_DEELX_DEELX_H__
